func (c Config) Load() (err error) { err = InitPath(ConfigPath) if err != nil { return } // If stat cannot be calculated, ConfigPath does not exist. This is not an error. if _, err = os.Stat(ConfigPath); err != nil { err = nil return } byt, err := ioutil.ReadFile(ConfigPath) if err != nil { err = util.LyciaError(fmt.Sprintf("access error to config path '%s': %s", ConfigPath, err)) return } var rawConfig []SiteConfig err = json.Unmarshal(byt, &rawConfig) if err != nil { err = util.LyciaError(fmt.Sprintf("config path '%s' is corrupted: %s", ConfigPath, err)) return } for _, siteConfig := range rawConfig { c[siteConfig.Host] = siteConfig } return }
func RemoteURL(dir string) (parsed *remoteURL, err error) { cmd := exec.Command("git", "remote", "-v") cmd.Dir = dir out, cmdErr := cmd.Output() outStr := string(out) if cmdErr != nil { msg := fmt.Sprintf("can not exec 'git remove -v' : %s", cmdErr) err = util.LyciaError(msg) } else if outStr == "" { err = util.LyciaError("git remote is not defined") } else if !remoteUrlPattern.MatchString(outStr) { msg := fmt.Sprintf("unknown git remote string: %s", outStr) err = util.LyciaError(msg) } else { rawUrl := remoteUrlPattern.FindStringSubmatch(outStr)[1] gitUrl, _ := UrlMaker(rawUrl) parsedURL, err := url.Parse(gitUrl.WebUrl) if err == nil { parsed = &remoteURL{"", parsedURL} } } return }
func (c Cache) SaveCache() (err error) { err = InitPath(CachePath) if err != nil { return } var rawCache []PrUrlCache for repositoryUrl, cache := range c { for branch, prUrl := range cache { rawCache = append(rawCache, PrUrlCache{repositoryUrl, branch, prUrl}) } } byt, err := json.Marshal(rawCache) if err != nil { err = util.LyciaError(fmt.Sprintf("cannot encode cache to JSON: %s", err)) return } err = ioutil.WriteFile(CachePath, byt, 0644) if err != nil { err = util.LyciaError(fmt.Sprintf("cannot write cache to file '%s': %s", CachePath, err)) } return }
func (repo *repository) PullrequestUrlWithBranch(branch string, force bool) (prURL *url.URL, err error) { if !force { prURL, err = repo.GetPrUrlFromCache(branch) if err == nil { return } } values := url.Values{} repoPath := strings.TrimLeft(repo.URL.Path, "/") queryString := fmt.Sprintf("repo:%s type:pr head:%s", repoPath, branch) values.Add("q", queryString) apiRoot, err := repo.DetectApiRootAndSetAccessToken(values) if err != nil { err = util.LyciaError("cannot detect ApiRoot: " + err.Error()) return } searchURL := fmt.Sprintf("%s/search/issues?%s", apiRoot, values.Encode()) res, err := http.Get(searchURL) if err != nil { err = util.LyciaError("failed to fetch: " + searchURL) return } defer res.Body.Close() decoder := json.NewDecoder(res.Body) var searchIssues SearchIssues if err = decoder.Decode(&searchIssues); err != nil { return } items := searchIssues.Items if len(items) == 0 { err = util.LyciaError(fmt.Sprintf("pullrequest not found for the branch: %s", branch)) return } prURL, err = url.Parse(items[0].HtmlUrl) if err != nil { err = util.LyciaError(fmt.Sprintf("html_url is invalid: %s", items[0].HtmlUrl)) return } err = repo.SavePrUrlToCache(branch, prURL) if err != nil { err = util.LyciaError(fmt.Sprintf("cache cannot be saved: %s", err)) } return }
func parseTmuxEnv() (setting Setting, err error) { showenvCommand := _exec.Command("tmux", "showenv") stdout, err := showenvCommand.StdoutPipe() if err != nil { err = util.LyciaError(err.Error()) return } err = showenvCommand.Start() if err != nil { err = util.LyciaError(err.Error()) return } scanner := bufio.NewScanner(stdout) const sep = "=" const prefix = "LC_FSSH_" var port, user, copyArgs, path string for scanner.Scan() { keyAndValue := strings.SplitN(scanner.Text(), sep, 2) if len(keyAndValue) != 2 { continue } key := keyAndValue[0] value := keyAndValue[1] if -1 == strings.Index(key, prefix) { continue } switch key[len(prefix):] { case "PORT": port = value case "USER": user = value case "COPY_ARGS": copyArgs = value case "PATH": path = value } } err = showenvCommand.Wait() if err != nil { err = util.LyciaError(err.Error()) return } if len(port) > 0 && len(user) > 0 && len(copyArgs) > 0 && len(path) > 0 { setting = Setting{port, user, copyArgs, path} } return }
func (repo *repository) GetPrUrlFromCache(branch string) (prURL *url.URL, err error) { branchToUrl, ok := repo.Cache[repo.URL.String()] if !ok { err = util.LyciaError("cache not found") return } if prUrlStr, ok := branchToUrl[branch]; ok { prURL, err = url.Parse(prUrlStr) } else { err = util.LyciaError("cache not found") } return }
func DetectCurrentBranch(dir string) (branch string) { cmd := exec.Command("git", "branch") cmd.Dir = dir out, err := cmd.Output() if err != nil { err = util.LyciaError("can not exec 'git branch'") return } if !branchPattern.Match(out) { err = util.LyciaError("can not detect branch") return } branch = string(branchPattern.FindSubmatch(out)[1]) return }
func InitPath(pathStr string) (err error) { dir, _ := path.Split(pathStr) stat, err := os.Stat(dir) if err != nil || !stat.IsDir() { err = os.MkdirAll(dir, 0755) if err != nil { err = util.LyciaError(fmt.Sprintf("cannot mkdir: '%s'", dir)) return } } return }
func (c Cache) LoadCache() (err error) { err = InitPath(CachePath) if err != nil { return } // If stat cannot be calculated, CachePath does not exist. This is not an error. if _, err = os.Stat(CachePath); err != nil { err = nil return } byt, err := ioutil.ReadFile(CachePath) if err != nil { err = util.LyciaError(fmt.Sprintf("access error to cache path '%s': %s", CachePath, err)) return } var rawCache []PrUrlCache err = json.Unmarshal(byt, &rawCache) if err != nil { err = util.LyciaError(fmt.Sprintf("cache path '%s' is corrupted: %s", CachePath, err)) return } for _, prUrlCache := range rawCache { var branchToUrl BranchToUrl if c[prUrlCache.RepositoryUrl] == nil { branchToUrl = make(BranchToUrl) c[prUrlCache.RepositoryUrl] = branchToUrl } else { branchToUrl = c[prUrlCache.RepositoryUrl] } branchToUrl[prUrlCache.Branch] = prUrlCache.PrUrl } return }
func (c Config) Save() (err error) { err = InitPath(ConfigPath) if err != nil { return } var rawConfig []SiteConfig for _, config := range c { rawConfig = append(rawConfig, config) } byt, err := json.Marshal(rawConfig) if err != nil { err = util.LyciaError(fmt.Sprintf("cannot encode config to JSON: %s", err)) return } err = ioutil.WriteFile(ConfigPath, byt, 0644) if err != nil { err = util.LyciaError(fmt.Sprintf("cannot write config to file '%s': %s", ConfigPath, err)) } return }
// Command is to execute any commands with FSSH func Command(name string, args ...string) (cmd *exec.Cmd, err error) { empty := Setting{} var fsshEnv Setting if fsshEnv, err = fetchFsshEnv(); err != nil { err = util.LyciaError(err.Error()) return } else if fsshEnv != empty { commandToExecute := strings.Join(append([]string{name}, args...), " ") name = "ssh" args = fsshEnv.sshArgs(commandToExecute) } cmd = _exec.Command(name, args...) return }
func (repo *repository) DetectApiRootAndSetAccessToken(values url.Values) (apiRoot string, err error) { if repo.URL.Host == "github.com" { apiRoot = "https://api.github.com" return } if v, ok := repo.Config[repo.URL.Host]; ok { apiRoot = v.ApiRoot values.Add("access_token", v.AccessToken) return } sc := SiteConfig{repo.URL.Host, "", ""} msg := fmt.Sprintf("Please input github API root path for '%s' (such as 'https://api.github.com') :", repo.URL.Host) apiRoot, err = ask.Ask(msg, false) if err != nil { return } sc.ApiRoot = strings.TrimLeft(apiRoot, "/") sc.AccessToken, err = repo.NewAccessToken(sc.Host, sc.ApiRoot) if err != nil { err = util.LyciaError("failed to generate new access token: " + err.Error()) return } repo.Config[repo.URL.Host] = sc err = repo.Config.Save() if err != nil { err = util.LyciaError("failed to save config: " + err.Error()) return } values.Add("access_token", sc.AccessToken) return }
func (gitUrl *GitUrl) Parse() (err error) { if !urlPattern.MatchString(gitUrl.RawUrl) { return util.LyciaError("this is not URL for git") } names := urlPattern.SubexpNames()[1:] m := urlPattern.FindStringSubmatch(gitUrl.RawUrl)[1:] matches := make(map[string]string) for i, str := range m { matches[names[i]] = str } gitUrl.Scheme = matches["scheme"] gitUrl.Username = matches["username"] gitUrl.Host = matches["host"] gitUrl.Path = matches["path"] return }
func (repo *repository) NewAccessToken(host string, apiRoot string) (accessToken string, err error) { username, err := ask.Ask(fmt.Sprintf("Please input username for '%s':", host), false) if err != nil { return } password, err := ask.Ask(fmt.Sprintf("Please input password for '%s':", host), true) if err != nil { return } reqBody := fmt.Sprintf(`{"scopes":["repo"],"note":"lycia %s"}`, time.Now()) req, err := repo.NewPostRequest(username, password, apiRoot+"/authorizations", reqBody) if err != nil { return } client := &http.Client{} res, err := client.Do(req) if err != nil { return } if res.StatusCode == 401 { if v := res.Header.Get("X-Github-Otp"); strings.HasPrefix(v, "required;") { var otp string otp, err = ask.Ask("Please input two-factor authentication code:", false) if err != nil { return } req, err = repo.NewPostRequest(username, password, apiRoot+"/authorizations", reqBody) if err != nil { return } req.Header.Add("X-Github-Otp", otp) res, err = client.Do(req) if err != nil { return } } else { err = util.LyciaError("Bad credentials") return } } else if res.StatusCode != 200 && res.StatusCode != 201 { err = util.LyciaError(fmt.Sprintf("Unknown status: %s", res.Status)) return } defer res.Body.Close() decoder := json.NewDecoder(res.Body) var authorizations Authorizations if err = decoder.Decode(&authorizations); err != nil { return } if authorizations.HashedToken != "" { accessToken = authorizations.HashedToken } else if authorizations.Token != "" { accessToken = authorizations.Token } else { err = util.LyciaError("cannot detect HashedToken") } return }