Beispiel #1
0
func (c *Configs) PromptFor(host string) *Credentials {
	cc := c.find(host)
	if cc == nil {
		user := c.PromptForUser()
		pass := c.PromptForPassword(host, user)

		// Create Client with a stub Credentials
		client := Client{Credentials: &Credentials{Host: host}}
		token, err := client.FindOrCreateToken(user, pass, "")
		if err != nil {
			if ce, ok := err.(*ClientError); ok && ce.Is2FAError() {
				code := c.PromptForOTP()
				token, err = client.FindOrCreateToken(user, pass, code)
			}
		}
		utils.Check(err)

		cc = &Credentials{Host: host, User: user, AccessToken: token}
		c.Credentials = append(c.Credentials, *cc)
		err = saveTo(configsFile(), c)
		utils.Check(err)
	}

	return cc
}
Beispiel #2
0
func (c *Config) FetchCredentials() {
	var changed bool
	if c.User == "" {
		c.FetchUser()
		changed = true
	}

	if c.Token == "" {
		password := c.FetchPassword()
		token, err := findOrCreateToken(c.User, password, "")
		// TODO: return an two factor auth failure error
		if err != nil {
			re := regexp.MustCompile("two-factor authentication OTP code")
			if re.MatchString(fmt.Sprintf("%s", err)) {
				code := c.FetchTwoFactorCode()
				token, err = findOrCreateToken(c.User, password, code)
			}
		}

		utils.Check(err)

		c.Token = token
		changed = true
	}

	if changed {
		err := SaveConfig(c)
		utils.Check(err)
	}
}
Beispiel #3
0
/*
  $ gh ci-status
  > (prints CI state of HEAD and exits with appropriate code)
  > One of: success (0), error (1), failure (1), pending (2), no status (3)

  $ gh ci-status -v
  > (prints CI state of HEAD, the URL to the CI build results and exits with appropriate code)
  > One of: success (0), error (1), failure (1), pending (2), no status (3)

  $ gh ci-status BRANCH
  > (prints CI state of BRANCH and exits with appropriate code)
  > One of: success (0), error (1), failure (1), pending (2), no status (3)

  $ gh ci-status SHA
  > (prints CI state of SHA and exits with appropriate code)
  > One of: success (0), error (1), failure (1), pending (2), no status (3)
*/
func ciStatus(cmd *Command, args *Args) {
	ref := "HEAD"
	if !args.IsParamsEmpty() {
		ref = args.RemoveParam(0)
	}

	localRepo := github.LocalRepo()
	project, err := localRepo.MainProject()
	utils.Check(err)

	sha, err := git.Ref(ref)
	if err != nil {
		err = fmt.Errorf("Aborted: no revision could be determined from '%s'", ref)
	}
	utils.Check(err)

	if args.Noop {
		fmt.Printf("Would request CI status for %s\n", sha)
	} else {
		state, targetURL, exitCode, err := fetchCiStatus(project, sha)
		utils.Check(err)
		if flagCiStatusVerbose && targetURL != "" {
			fmt.Printf("%s: %s\n", state, targetURL)
		} else {
			fmt.Println(state)
		}

		os.Exit(exitCode)
	}
}
Beispiel #4
0
/*
  $ gh ci-status
  > (prints CI state of HEAD and exits with appropriate code)
  > One of: success (0), error (1), failure (1), pending (2), no status (3)

  $ gh ci-status BRANCH
  > (prints CI state of BRANCH and exits with appropriate code)
  > One of: success (0), error (1), failure (1), pending (2), no status (3)

  $ gh ci-status SHA
  > (prints CI state of SHA and exits with appropriate code)
  > One of: success (0), error (1), failure (1), pending (2), no status (3)
*/
func ciStatus(cmd *Command, args *Args) {
	ref := "HEAD"
	if !args.IsParamsEmpty() {
		ref = args.RemoveParam(0)
	}

	ref, err := git.Ref(ref)
	utils.Check(err)

	args.Replace("", "")
	if args.Noop {
		fmt.Printf("Would request CI status for %s\n", ref)
	} else {
		state, targetURL, desc, exitCode, err := fetchCiStatus(ref)
		utils.Check(err)
		fmt.Println(state)
		if targetURL != "" {
			fmt.Println(targetURL)
		}
		if desc != "" {
			fmt.Println(desc)
		}

		os.Exit(exitCode)
	}
}
Beispiel #5
0
func parseOwnerAndName() (name, remote string) {
	remote, err := git.Remote()
	utils.Check(err)

	url, err := mustMatchGitHubURL(remote)
	utils.Check(err)

	return url[1], url[2]
}
Beispiel #6
0
/*
  $ gh create
  ... create repo on github ...
  > git remote add -f origin [email protected]:YOUR_USER/CURRENT_REPO.git

  # with description:
  $ gh create -d 'It shall be mine, all mine!'

  $ gh create recipes
  [ repo created on GitHub ]
  > git remote add origin [email protected]:YOUR_USER/recipes.git

  $ gh create sinatra/recipes
  [ repo created in GitHub organization ]
  > git remote add origin [email protected]:sinatra/recipes.git
*/
func create(command *Command, args *Args) {
	_, err := git.Dir()
	if err != nil {
		err = fmt.Errorf("'create' must be run from inside a git repository")
		utils.Check(err)
	}

	var newRepoName string
	if args.IsParamsEmpty() {
		newRepoName, err = utils.DirName()
		utils.Check(err)
	} else {
		reg := regexp.MustCompile("^[^-]")
		if !reg.MatchString(args.FirstParam()) {
			err = fmt.Errorf("invalid argument: %s", args.FirstParam())
			utils.Check(err)
		}
		newRepoName = args.FirstParam()
	}

	configs := github.CurrentConfigs()
	credentials := configs.DefaultCredentials()

	owner := credentials.User
	if strings.Contains(newRepoName, "/") {
		split := strings.SplitN(newRepoName, "/", 2)
		owner = split[0]
		newRepoName = split[1]
	}

	project := github.NewProject(owner, newRepoName, credentials.Host)
	gh := github.NewClient(project.Host)

	var action string
	if gh.IsRepositoryExist(project) {
		fmt.Printf("%s already exists on %s\n", project, project.Host)
		action = "set remote origin"
	} else {
		action = "created repository"
		if !args.Noop {
			repo, err := gh.CreateRepository(project, flagCreateDescription, flagCreateHomepage, flagCreatePrivate)
			utils.Check(err)
			project = github.NewProject(repo.FullName, "", project.Host)
		}
	}

	remote, _ := github.OriginRemote()
	if remote == nil {
		url := project.GitURL("", "", true)
		args.Replace("git", "remote", "add", "-f", "origin", url)
	} else {
		args.Replace("git", "remote", "-v")
	}

	args.After("echo", fmt.Sprintf("%s:", action), project.String())
}
Beispiel #7
0
func alias(command *Command, args *Args) {
	var shell string
	if args.ParamsSize() > 0 {
		shell = args.FirstParam()
	} else {
		shell = os.Getenv("SHELL")
	}

	if shell == "" {
		utils.Check(fmt.Errorf("Unknown shell"))
	}

	shells := []string{"bash", "zsh", "sh", "ksh", "csh", "fish"}
	shell = filepath.Base(shell)
	var validShell bool
	for _, s := range shells {
		if s == shell {
			validShell = true
			break
		}
	}

	if !validShell {
		err := fmt.Errorf("Unsupported shell. Supported shell: %s.", strings.Join(shells, ", "))
		utils.Check(err)
	}

	if flagAliasScript {
		fmt.Println("alias git=gh")
		if "zsh" == shell {
			fmt.Println("if type compdef > /dev/null; then")
			fmt.Println("  compdef gh=git")
			fmt.Println("fi")
		}
	} else {
		var profile string
		switch shell {
		case "bash":
			profile = "~/.bash_profile"
		case "zsh":
			profile = "~/.zshrc"
		case "ksh":
			profile = "~/.profile"
		default:
			profile = "your profile"
		}

		msg := fmt.Sprintf("# Wrap git automatically by adding the following to %s\n", profile)
		fmt.Println(msg)
		fmt.Println("eval  \"$(gh alias -s)\"")
	}

	os.Exit(0)
}
Beispiel #8
0
func report(reportedError error, stack string) {
	title, body, err := reportTitleAndBody(reportedError, stack)
	utils.Check(err)

	project := NewProject(ghProjectOwner, ghProjectName, GitHubHost)

	gh := NewClient(project.Host)

	issue, err := gh.CreateIssue(project, title, body, []string{"Crash Report"})
	utils.Check(err)

	fmt.Println(issue.HTMLURL)
}
Beispiel #9
0
/*
  $ gh browse
  > open https://github.com/YOUR_USER/CURRENT_REPO

  $ gh browse commit/SHA
  > open https://github.com/YOUR_USER/CURRENT_REPO/commit/SHA

  $ gh browse issues
  > open https://github.com/YOUR_USER/CURRENT_REPO/issues

  $ gh browse -p jingweno/gh
  > open https://github.com/jingweno/gh

  $ gh browse -p jingweno/gh commit/SHA
  > open https://github.com/jingweno/gh/commit/SHA

  $ gh browse -p resque
  > open https://github.com/YOUR_USER/resque

  $ gh browse -p resque network
  > open https://github.com/YOUR_USER/resque/network
*/
func browse(command *Command, args *Args) {
	var (
		project *github.Project
		branch  *github.Branch
		err     error
	)
	localRepo := github.LocalRepo()
	if flagBrowseProject != "" {
		// gh browse -p jingweno/gh
		// gh browse -p gh
		project = github.NewProject("", flagBrowseProject, "")
	} else {
		// gh browse
		branch, project, err = localRepo.RemoteBranchAndProject("")
		utils.Check(err)
	}

	if project == nil {
		err := fmt.Errorf(command.FormattedUsage())
		utils.Check(err)
	}

	master := localRepo.MasterBranch()
	if branch == nil {
		branch = master
	}

	var subpage string
	if !args.IsParamsEmpty() {
		subpage = args.RemoveParam(0)
	}

	if subpage == "commits" {
		subpage = fmt.Sprintf("commits/%s", branchInURL(branch))
	} else if subpage == "tree" || subpage == "" {
		if !reflect.DeepEqual(branch, master) && branch.IsRemote() {
			subpage = fmt.Sprintf("tree/%s", branchInURL(branch))
		}
	}

	pageUrl := project.WebURL("", "", subpage)
	launcher, err := utils.BrowserLauncher()
	utils.Check(err)

	if flagBrowseURLOnly {
		args.Replace("echo", pageUrl)
	} else {
		args.Replace(launcher[0], "", launcher[1:]...)
		args.AppendParams(pageUrl)
	}
}
Beispiel #10
0
/*
  $ gh issue
*/
func issue(cmd *Command, args *Args) {

	gh := github.New()

	// list all issues

	if args.Noop {
		fmt.Printf("Would request list of issues for %s\n", gh.Project)
	} else {
		issues, err := gh.Issues()
		utils.Check(err)
		for _, issue := range issues {
			var url string
			// use the pull request URL if we have one
			if issue.PullRequest.HTMLURL != "" {
				url = issue.PullRequest.HTMLURL
			} else {
				url = issue.HTMLURL
			}
			// "nobody" should have more than 1 million github issues
			fmt.Printf("% 7d] %s ( %s )\n", issue.Number, issue.Title, url)
		}
	}

	os.Exit(0)
}
Beispiel #11
0
func tranformFetchArgs(args *Args) {
	remotes, err := git.Remotes()
	utils.Check(err)

	names := parseRemoteNames(args)
	gh := github.New()
	projects := []github.Project{}
	ownerRegexp := regexp.MustCompile(OwnerRe)
	for _, name := range names {
		if ownerRegexp.MatchString(name) && !isRemoteExist(remotes, name) {
			project := github.NewProjectFromNameAndOwner("", name)
			repo, err := gh.Repository(project)
			if err != nil {
				continue
			}

			project = github.NewProjectFromNameAndOwner("", repo.FullName)
			projects = append(projects, project)
		}
	}

	for _, project := range projects {
		var isSSH bool
		if project.Owner == gh.Config.FetchUser() {
			isSSH = true
		}
		args.Before("git", "remote", "add", project.Owner, project.GitURL("", "", isSSH))
	}
}
Beispiel #12
0
func transformRemoteArgs(args *Args) {
	ownerWithName := args.LastParam()
	owner, name, match := parseRepoNameOwner(ownerWithName)
	if !match {
		return
	}

	var err error
	if name == "" {
		name, err = utils.DirName()
		utils.Check(err)
	}

	isPriavte := parseRemotePrivateFlag(args)

	if owner == "origin" {
		owner = github.CurrentConfig().FetchUser()
	} else if args.ParamsSize() > 2 {
		// `git remote add jingweno foo/bar`
		args.RemoveParam(args.ParamsSize() - 1)
	}

	project := github.Project{Owner: owner, Name: name}
	url := project.GitURL(name, owner, isPriavte)

	args.AppendParams(url)
}
Beispiel #13
0
func CurrentProject() *Project {
	remote, err := git.OriginRemote()
	utils.Check(err)

	owner, name := parseOwnerAndName(remote.URL)

	return &Project{name, owner}
}
Beispiel #14
0
Datei: ci.go Projekt: prsimp/gh
func ci(cmd *Command, args []string) {
	ref := "HEAD"
	if len(args) > 0 {
		ref = args[0]
	}

	ref, err := git.Ref(ref)
	utils.Check(err)

	gh := github.New()
	status, err := gh.CiStatus(ref)
	utils.Check(err)

	var state string
	var targetURL string
	var desc string
	var exitCode int
	if status == nil {
		state = "no status"
	} else {
		state = status.State
		targetURL = status.TargetURL
		desc = status.Description
	}

	switch state {
	case "success":
		exitCode = 0
	case "failure", "error":
		exitCode = 1
	case "pending":
		exitCode = 2
	default:
		exitCode = 3
	}

	fmt.Println(state)
	if targetURL != "" {
		fmt.Println(targetURL)
	}
	if desc != "" {
		fmt.Println(desc)
	}

	os.Exit(exitCode)
}
Beispiel #15
0
func detectContentType(path string, fi os.FileInfo) string {
	file, err := os.Open(path)
	utils.Check(err)
	defer file.Close()

	fileHeader := &bytes.Buffer{}
	headerSize := int64(512)
	if fi.Size() < headerSize {
		headerSize = fi.Size()
	}

	// The content type detection only uses 512 bytes at most.
	// This way we avoid copying the whole content for big files.
	_, err = io.CopyN(fileHeader, file, headerSize)
	utils.Check(err)

	return http.DetectContentType(fileHeader.Bytes())
}
Beispiel #16
0
func (gh *GitHub) client() *octokat.Client {
	config := gh.config
	if config.User == "" {
		config.FetchUser()
	}

	if config.Token == "" {
		password := config.FetchPassword()
		token, err := findOrCreateToken(config.User, password)
		utils.Check(err)

		config.Token = token
		err = saveConfig(config)
		utils.Check(err)
	}

	return octokat.NewClient().WithToken(config.Token)
}
Beispiel #17
0
func runVersion(cmd *Command, args []string) {
	gitVersion, err := git.Version()
	utils.Check(err)

	ghVersion := fmt.Sprintf("gh version %s", Version)

	fmt.Println(gitVersion)
	fmt.Println(ghVersion)
}
Beispiel #18
0
func runInLocalRepo(fn func(localRepo *github.GitHubRepo, project *github.Project, client *github.Client)) {
	localRepo := github.LocalRepo()
	project, err := localRepo.CurrentProject()
	utils.Check(err)

	client := github.NewClient(project.Host)
	fn(localRepo, project, client)

	os.Exit(0)
}
Beispiel #19
0
// Remove the scheme from host when the credential url is absolute.
func rawHost(host string) string {
	u, err := url.Parse(host)
	utils.Check(err)

	if u.IsAbs() {
		return u.Host
	} else {
		return u.Path
	}
}
Beispiel #20
0
func uploadReleaseAssets(gh *github.Client, release *octokit.Release, assetsDir string) {
	var wg sync.WaitGroup
	var totalAssets, countAssets uint64

	filepath.Walk(assetsDir, func(path string, fi os.FileInfo, err error) error {
		if !fi.IsDir() {
			totalAssets += 1
		}
		return nil
	})

	printUploadProgress(&countAssets, totalAssets)

	filepath.Walk(assetsDir, func(path string, fi os.FileInfo, err error) error {
		if !fi.IsDir() {
			wg.Add(1)

			go func() {
				defer func() {
					atomic.AddUint64(&countAssets, uint64(1))
					printUploadProgress(&countAssets, totalAssets)
					wg.Done()
				}()

				uploadUrl, err := release.UploadURL.Expand(octokit.M{"name": fi.Name()})
				utils.Check(err)

				contentType := detectContentType(path, fi)

				file, err := os.Open(path)
				utils.Check(err)
				defer file.Close()

				err = gh.UploadReleaseAsset(uploadUrl, file, contentType)
				utils.Check(err)
			}()
		}

		return nil
	})

	wg.Wait()
}
Beispiel #21
0
/*
  $ gh compare refactor
  > open https://github.com/CURRENT_REPO/compare/refactor

  $ gh compare 1.0..1.1
  > open https://github.com/CURRENT_REPO/compare/1.0...1.1

  $ gh compare -u other-user patch
  > open https://github.com/other-user/REPO/compare/patch
*/
func compare(command *Command, args *Args) {
	localRepo := github.LocalRepo()
	var (
		branch  *github.Branch
		project *github.Project
		r       string
		err     error
	)

	branch, project, err = localRepo.RemoteBranchAndProject("")
	utils.Check(err)

	if args.IsParamsEmpty() {
		master := localRepo.MasterBranch()
		if master.ShortName() == branch.ShortName() {
			err = fmt.Errorf(command.FormattedUsage())
			utils.Check(err)
		} else {
			r = branch.ShortName()
		}
	} else {
		r = parseCompareRange(args.RemoveParam(args.ParamsSize() - 1))
		if args.IsParamsEmpty() {
			project, err = localRepo.CurrentProject()
			utils.Check(err)
		} else {
			project = github.NewProject(args.RemoveParam(args.ParamsSize()-1), "", "")
		}
	}

	r = strings.Replace(r, "/", ";", -1)
	subpage := utils.ConcatPaths("compare", r)
	url := project.WebURL("", "", subpage)
	launcher, err := utils.BrowserLauncher()
	utils.Check(err)

	if flagCompareURLOnly {
		args.Replace("echo", url)
	} else {
		args.Replace(launcher[0], "", launcher[1:]...)
		args.AppendParams(url)
	}
}
Beispiel #22
0
func hasGitRemote(name string) bool {
	remotes, err := github.Remotes()
	utils.Check(err)
	for _, remote := range remotes {
		if remote.Name == name {
			return true
		}
	}

	return false
}
Beispiel #23
0
func New(cmd string) *Cmd {
	cmds, err := shellquote.Split(cmd)
	utils.Check(err)

	name := cmds[0]
	args := make([]string, 0)
	for _, arg := range cmds[1:] {
		args = append(args, arg)
	}
	return &Cmd{Name: name, Args: args}
}
Beispiel #24
0
func (c *Config) FetchPassword() string {
	msg := fmt.Sprintf("%s password for %s (never stored): ", GitHubHost, c.User)
	fmt.Print(msg)

	pass := gopass.GetPasswd()
	if len(pass) == 0 {
		utils.Check(errors.New("Password cannot be empty"))
	}

	return string(pass)
}
Beispiel #25
0
Datei: fork.go Projekt: prsimp/gh
func fork(cmd *Command, args []string) {
	gh := github.New()
	project := gh.Project

	newRemote, err := gh.ForkRepository(project.Name, project.Owner, flagForkNoRemote)
	utils.Check(err)

	if !flagForkNoRemote && newRemote != "" {
		fmt.Printf("New remote: %s\n", newRemote)
	}
}
Beispiel #26
0
func runVersion(cmd *Command, args *Args) {
	gitVersion, err := git.Version()
	utils.Check(err)

	ghVersion := fmt.Sprintf("gh version %s", Version)

	fmt.Println(gitVersion)
	fmt.Println(ghVersion)

	os.Exit(0)
}
Beispiel #27
0
func createIssue(cmd *Command, args *Args) {
	runInLocalRepo(func(localRepo *github.GitHubRepo, project *github.Project, gh *github.Client) {
		if args.Noop {
			fmt.Printf("Would create an issue for %s\n", project)
		} else {
			title, body, err := getTitleAndBodyFromFlags(flagIssueMessage, flagIssueFile)
			utils.Check(err)

			if title == "" {
				title, body, err = writeIssueTitleAndBody(project)
				utils.Check(err)
			}

			issue, err := gh.CreateIssue(project, title, body, flagIssueLabels)
			utils.Check(err)

			fmt.Println(issue.HTMLURL)
		}
	})
}
Beispiel #28
0
func (p *Project) LocalRepoWith(base, head string) *Repo {
	if base == "" {
		base = "master"
	}
	if head == "" {
		headBranch, err := git.Head()
		utils.Check(err)
		head = headBranch.ShortName()
	}

	return &Repo{base, head, p}
}
Beispiel #29
0
func (c *Config) FetchCredentials() {
	var changed bool
	if c.User == "" {
		c.FetchUser()
		changed = true
	}

	if c.Token == "" {
		password := c.FetchPassword()
		token, err := findOrCreateToken(c.User, password)
		utils.Check(err)

		c.Token = token
		changed = true
	}

	if changed {
		err := SaveConfig(c)
		utils.Check(err)
	}
}
Beispiel #30
0
/*
  # while on a topic branch called "feature":
  $ gh pull-request
  [ opens text editor to edit title & body for the request ]
  [ opened pull request on GitHub for "YOUR_USER:feature" ]

  # explicit pull base & head:
  $ gh pull-request -b jingweno:master -h jingweno:feature

  $ gh pull-request -i 123
  [ attached pull request to issue #123 ]
*/
func pullRequest(cmd *Command, args *Args) {
	var title, body string
	if args.ParamsSize() == 1 {
		title = args.RemoveParam(0)
	}

	gh := github.New()
	repo := gh.Project.LocalRepoWith(flagPullRequestBase, flagPullRequestHead)
	if title == "" && flagPullRequestIssue == "" {
		t, b, err := writePullRequestTitleAndBody(repo)
		utils.Check(err)
		title = t
		body = b
	}

	if title == "" && flagPullRequestIssue == "" {
		utils.Check(fmt.Errorf("Aborting due to empty pull request title"))
	}

	var pullRequestURL string
	if args.Noop {
		args.Before(fmt.Sprintf("Would request a pull request to %s from %s", repo.FullBase(), repo.FullHead()), "")
		pullRequestURL = "PULL_REQUEST_URL"
	} else {
		if title != "" {
			pr, err := gh.CreatePullRequest(repo.Base, repo.Head, title, body)
			utils.Check(err)
			pullRequestURL = pr.HTMLURL
		}

		if flagPullRequestIssue != "" {
			pr, err := gh.CreatePullRequestForIssue(repo.Base, repo.Head, flagPullRequestIssue)
			utils.Check(err)
			pullRequestURL = pr.HTMLURL
		}
	}

	args.Replace("echo", "", pullRequestURL)
}