Beispiel #1
0
func installPlugins(l *log.Logger, errChan chan errMsg) {
	if err := core.LoadConf(); err != nil {
		errChan <- errMsg{msg: "", err: err}
		return
	}
	plugins := buildPluginFile(l)

	// Fetch all plugins
	if len(plugins.Dependencies) == 1 {
		l.Infof("Fetching 1 plugin...\n")
	} else {
		l.Infof("Fetching %d plugins...\n", len(plugins.Dependencies))
	}
	outC, err := exec.
		Command("/bin/sh", "-c", "go get ./...").
		CombinedOutput()
	if err == nil {
		syncDependencies(plugins, l, errChan)
		return
	}

	// Show errors only when it's not a private repo issue
	if !strings.Contains(string(outC), "fatal: could not read Username for") {
		errChan <- errMsg{msg: string(outC), err: err}
		return
	}

	// TODO enable versioning for private repos
	l.Infof("Fetching private repos...\n\n")
	l.Info("*** This will delete your local plugins to fetch remote copies.")
	_, err = fmt.Print("Continue? [n]: ")
	if err != nil {
		errChan <- errMsg{msg: "", err: err}
		return
	}
	reader := bufio.NewReader(os.Stdin)
	text, err := reader.ReadString('\n')
	if err != nil {
		errChan <- errMsg{msg: "", err: errors.New("failed to read from stdin")}
		return
	}
	if text[0] != 'y' && text[0] != 'Y' {
		errChan <- errMsg{msg: "Canceled", err: nil}
		return
	}
	wg := &sync.WaitGroup{}
	for name, _ := range plugins.Dependencies {
		go clonePrivateRepo(name, errChan, wg)
	}
	wg.Wait()
	syncDependencies(plugins, l, errChan)
}
Beispiel #2
0
func buildPluginFile(l *log.Logger) *core.PluginJSON {
	// Create plugins.go file, truncate if exists
	fi, err := os.Create("plugins.go")
	if err != nil {
		l.Fatal(err)
	}
	defer func() {
		if err = fi.Close(); err != nil {
			l.Fatal(err)
		}
	}()

	s := "// This file is generated by `abot plugin install`. Do not edit.\n"
	s += "package main\n\nimport (\n"
	for url := range core.Conf().Dependencies {
		// Insert _ imports
		s += fmt.Sprintf("\t_ \"%s\"\n", url)
	}
	s += ")"
	_, err = fi.WriteString(s)
	if err != nil {
		l.Fatal(err)
	}

	return core.Conf()
}
Beispiel #3
0
func embedPluginConfs(plugins *core.PluginJSON, l *log.Logger) {
	log.Debug("embedding plugin confs")

	// Open plugins.go file for writing
	fi, err := os.OpenFile("plugins.go", os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		l.Fatal(err)
	}
	defer func() {
		if err = fi.Close(); err != nil {
			l.Fatal(err)
		}
	}()

	p := os.Getenv("GOPATH")
	tokenizedPath := strings.Split(p, string(os.PathListSeparator))

	// Insert plugin.json text as comments
	s := "\n\n/*\n"
	for u := range plugins.Dependencies {
		s += u + "\n"
		log.Debug("reading file", p)
		p = filepath.Join(tokenizedPath[0], "src", u, "plugin.json")
		fi2, err2 := os.Open(p)
		if err2 != nil {
			l.Fatal(err2)
		}
		scn := bufio.NewScanner(fi2)
		var tmp string
		for scn.Scan() {
			line := scn.Text() + "\n"
			s += line
			tmp += line
		}
		if err2 = scn.Err(); err2 != nil {
			l.Fatal(err2)
		}
		if err2 = fi2.Close(); err2 != nil {
			l.Fatal(err2)
		}

		var plg struct{ Name string }
		if err2 = json.Unmarshal([]byte(tmp), &plg); err2 != nil {
			l.Fatal(err2)
		}

		// Fetch remote plugin IDs to be included in the plugin confs
		plg.Name = url.QueryEscape(plg.Name)
		ul := os.Getenv("ITSABOT_URL") + "/api/plugins/by_name/" + plg.Name
		req, err2 := http.NewRequest("GET", ul, nil)
		if err2 != nil {
			l.Fatal(err2)
		}
		client := &http.Client{Timeout: 10 * time.Second}
		resp, err2 := client.Do(req)
		if err2 != nil {
			l.Fatal(err2)
		}
		var data struct{ ID uint64 }
		if err2 := json.NewDecoder(resp.Body).Decode(&data); err2 != nil {
			l.Fatal(err2)
		}
		id := strconv.FormatUint(data.ID, 10)

		// Remove closing characters to insert additional ID data
		s = s[:len(s)-3]
		s += ",\n\t\"ID\": " + id + "\n}\n"
	}
	s += "*/"
	_, err = fi.WriteString(s)
	if err != nil {
		l.Fatal(err)
	}
}
Beispiel #4
0
func updateGlockfileAndInstall(l *log.Logger) {
	outC, err := exec.
		Command("/bin/sh", "-c", `pwd | sed "s|$GOPATH/src/||"`).
		CombinedOutput()
	if err != nil {
		l.Info(string(outC))
		l.Fatal(err)
	}

	// Update plugin dependency versions in GLOCKFILE
	p := string(outC)
	outC, err = exec.
		Command("/bin/sh", "-c", "glock save "+p).
		CombinedOutput()
	if err != nil {
		l.Info(string(outC))
		l.Fatal(err)
	}

	outC, err = exec.
		Command("/bin/sh", "-c", "go install").
		CombinedOutput()
	if err != nil {
		l.Info(string(outC))
		l.Fatal(err)
	}
}
Beispiel #5
0
func syncDependencies(plugins *core.PluginJSON, l *log.Logger,
	errChan chan errMsg) {

	// Sync each of them to get dependencies
	wg := &sync.WaitGroup{}
	rand.Seed(time.Now().UTC().UnixNano())
	for url, version := range plugins.Dependencies {
		wg.Add(1)
		go func(url, version string) {
			defer wg.Done()
			// Check out specific commit
			var outB []byte
			var errB error
			if version != "*" {
				l.Debug("checking out", url, "at", version)
				p := filepath.Join(os.Getenv("GOPATH"), "src",
					url)
				c := fmt.Sprintf("git -C %s checkout %s", p, version)
				outB, errB = exec.
					Command("/bin/sh", "-c", c).
					CombinedOutput()
				if errB != nil {
					l.Debug(string(outB))
					errChan <- errMsg{msg: "", err: errB}
					return
				}
			}

			// Anonymously increment the plugin's download count
			// at itsabot.org
			l.Debug("incrementing download count", url)
			p := struct{ Path string }{Path: url}
			outB, errB = json.Marshal(p)
			if errB != nil {
				errChan <- errMsg{msg: "failed to build itsabot.org JSON.", err: errB}
				return
			}
			var u string
			u = os.Getenv("ITSABOT_URL") + "/api/plugins.json"
			req, errB := http.NewRequest("PUT", u, bytes.NewBuffer(outB))
			if errB != nil {
				errChan <- errMsg{msg: "failed to build request to itsabot.org.", err: errB}
				return
			}
			client := &http.Client{Timeout: 10 * time.Second}
			resp, errB := client.Do(req)
			if errB != nil {
				errChan <- errMsg{msg: "failed to update itsabot.org.", err: errB}
				return
			}
			defer func() {
				if errB = resp.Body.Close(); errB != nil {
					errChan <- errMsg{msg: "", err: errB}
				}
			}()
			if resp.StatusCode != 200 {
				l.Infof("WARN: %d - %s\n", resp.StatusCode,
					resp.Status)
			}
		}(url, version)
	}
	wg.Wait()

	// Ensure dependencies are still there with the latest checked out
	// versions, and install the plugins
	l.Info("Installing plugins...")
	outC, err := exec.
		Command("/bin/sh", "-c", "go get ./...").
		CombinedOutput()
	if err != nil {
		errChan <- errMsg{msg: string(outC), err: err}
		return
	}
	embedPluginConfs(plugins, l)
	updateGlockfileAndInstall(l)
	errChan <- errMsg{msg: "Success!"}
}
Beispiel #6
0
// newAbot creates a new directory for a new Abot project. It's similar to
// `rails new`.
func newAbot(l *log.Logger, name, dbconnstr string) error {
	// Create a new directory for the project
	if err := os.Mkdir(name, 0777); err != nil {
		return err
	}
	if err := os.Chdir(name); err != nil {
		return err
	}

	// Generate abot.env
	fi, err := os.Create("abot.env")
	if err != nil {
		return err
	}
	defer func() {
		if err = fi.Close(); err != nil {
			l.Info("failed to close abot.env.", err)
		}
	}()
	dir, err := os.Getwd()
	if err != nil {
		return err
	}
	_, err = fi.WriteString(serverAbotEnv(name, dir))
	if err != nil {
		return err
	}

	// Copy and modify base files
	p := filepath.Join(os.Getenv("ABOT_PATH"), "base", "plugins.json")
	if err = core.CopyFileContents(p, "plugins.json"); err != nil {
		return err
	}
	p = filepath.Join(os.Getenv("ABOT_PATH"), "base", "server.go.x")
	if err = core.CopyFileContents(p, "server.go"); err != nil {
		return err
	}
	p = filepath.Join(os.Getenv("ABOT_PATH"), "base", ".gitignore")
	if err = core.CopyFileContents(p, ".gitignore"); err != nil {
		return err
	}
	fi2, err := os.OpenFile(".gitignore", os.O_APPEND|os.O_WRONLY, 0666)
	if err != nil {
		return err
	}
	defer func() {
		if err = fi2.Close(); err != nil {
			l.Info("failed to close .gitignore.", err)
		}
	}()
	_, err = fi2.WriteString(name)
	if err != nil {
		l.Fatal("failed to write to .gitignore.", err)
	}

	// Walk the base/assets dir, copying all files
	p = filepath.Join(os.Getenv("ABOT_PATH"), "base", "assets")
	if err = filepath.Walk(p, recursiveCopy); err != nil {
		return err
	}
	p = filepath.Join(os.Getenv("ABOT_PATH"), "base", "cmd")
	if err = filepath.Walk(p, recursiveCopy); err != nil {
		return err
	}
	p = filepath.Join(os.Getenv("ABOT_PATH"), "base", "data")
	if err = filepath.Walk(p, recursiveCopy); err != nil {
		return err
	}
	p = filepath.Join(os.Getenv("ABOT_PATH"), "base", "db")
	if err = filepath.Walk(p, recursiveCopy); err != nil {
		return err
	}

	// Run cmd/dbsetup.sh
	cmd := exec.Command("/bin/sh", "-c", "cmd/dbsetup.sh "+dbconnstr)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err = cmd.Run(); err != nil {
		l.Info("Fix the errors above, then re-run cmd/dbsetup.sh")
		return err
	}

	// TODO analytics on a new Abot project
	return nil
}