func editRelease(cmd *Command, args *Args) { tagName := cmd.Arg(0) if tagName == "" { utils.Check(fmt.Errorf("Missing argument TAG")) return } localRepo, err := github.LocalRepo() utils.Check(err) project, err := localRepo.CurrentProject() utils.Check(err) gh := github.NewClient(project.Host) release, err := gh.FetchRelease(project, tagName) utils.Check(err) params := map[string]interface{}{} commitish := release.TargetCommitish if cmd.FlagPassed("commitish") { params["target_commitish"] = flagReleaseCommitish commitish = flagReleaseCommitish } if cmd.FlagPassed("draft") { params["draft"] = flagReleaseDraft } if cmd.FlagPassed("prerelease") { params["prerelease"] = flagReleasePrerelease } var title string var body string var editor *github.Editor if cmd.FlagPassed("message") { title, body = readMsg(flagReleaseMessage) } else if cmd.FlagPassed("file") { title, body, editor, err = readMsgFromFile(flagReleaseFile, flagReleaseEdit, "RELEASE", "release") utils.Check(err) if title == "" { utils.Check(fmt.Errorf("Aborting editing due to empty release title")) } } else { cs := git.CommentChar() message, err := renderReleaseTpl("Editing", cs, tagName, project.String(), commitish) utils.Check(err) message = fmt.Sprintf("%s\n\n%s\n%s", release.Name, release.Body, message) editor, err := github.NewEditor("RELEASE", "release", message) utils.Check(err) title, body, err = editor.EditTitleAndBody() utils.Check(err) if title == "" { utils.Check(fmt.Errorf("Aborting editing due to empty release title")) } } if title != "" { params["name"] = title } if body != "" { params["body"] = body } if len(params) > 0 { if args.Noop { ui.Printf("Would edit release `%s'\n", tagName) } else { release, err = gh.EditRelease(release, params) utils.Check(err) } if editor != nil { editor.DeleteFile() } } uploadAssets(gh, release, flagReleaseAssets, args) args.NoForward() }
/* # 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 -m "title\n\nbody" [ create pull request with title & body ] $ gh pull-request -i 123 [ attached pull request to issue #123 ] $ gh pull-request https://github.com/jingweno/gh/pull/123 [ attached pull request to issue #123 ] $ gh pull-request -F FILE [ create pull request with title & body from FILE ] */ func pullRequest(cmd *Command, args *Args) { localRepo, err := github.LocalRepo() utils.Check(err) currentBranch, err := localRepo.CurrentBranch() utils.Check(err) baseProject, err := localRepo.MainProject() utils.Check(err) host, err := github.CurrentConfig().PromptForHost(baseProject.Host) if err != nil { utils.Check(github.FormatError("creating pull request", err)) } trackedBranch, headProject, err := localRepo.RemoteBranchAndProject(host.User, false) utils.Check(err) var ( base, head string force bool ) force = flagPullRequestForce if flagPullRequestBase != "" { baseProject, base = parsePullRequestProject(baseProject, flagPullRequestBase) } if flagPullRequestHead != "" { headProject, head = parsePullRequestProject(headProject, flagPullRequestHead) } if args.ParamsSize() == 1 { arg := args.RemoveParam(0) flagPullRequestIssue = parsePullRequestIssueNumber(arg) } if base == "" { masterBranch := localRepo.MasterBranch() base = masterBranch.ShortName() } if head == "" && trackedBranch != nil { if !trackedBranch.IsRemote() { // the current branch tracking another branch // pretend there's no upstream at all trackedBranch = nil } else { if baseProject.SameAs(headProject) && base == trackedBranch.ShortName() { e := fmt.Errorf(`Aborted: head branch is the same as base ("%s")`, base) e = fmt.Errorf("%s\n(use `-h <branch>` to specify an explicit pull request head)", e) utils.Check(e) } } } if head == "" { if trackedBranch == nil { head = currentBranch.ShortName() } else { head = trackedBranch.ShortName() } } title, body, err := getTitleAndBodyFromFlags(flagPullRequestMessage, flagPullRequestFile) utils.Check(err) fullBase := fmt.Sprintf("%s:%s", baseProject.Owner, base) fullHead := fmt.Sprintf("%s:%s", headProject.Owner, head) if !force && trackedBranch != nil { remoteCommits, _ := git.RefList(trackedBranch.LongName(), "") if len(remoteCommits) > 0 { err = fmt.Errorf("Aborted: %d commits are not yet pushed to %s", len(remoteCommits), trackedBranch.LongName()) err = fmt.Errorf("%s\n(use `-f` to force submit a pull request anyway)", err) utils.Check(err) } } var editor *github.Editor if title == "" && flagPullRequestIssue == "" { baseTracking := base headTracking := head remote := gitRemoteForProject(baseProject) if remote != nil { baseTracking = fmt.Sprintf("%s/%s", remote.Name, base) } if remote == nil || !baseProject.SameAs(headProject) { remote = gitRemoteForProject(headProject) } if remote != nil { headTracking = fmt.Sprintf("%s/%s", remote.Name, head) } message, err := pullRequestChangesMessage(baseTracking, headTracking, fullBase, fullHead) utils.Check(err) editor, err = github.NewEditor("PULLREQ", "pull request", message) utils.Check(err) title, body, err = editor.EditTitleAndBody() utils.Check(err) } 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", fullBase, fullHead), "") pullRequestURL = "PULL_REQUEST_URL" } else { var ( pr *octokit.PullRequest err error ) client := github.NewClientWithHost(host) if title != "" { pr, err = client.CreatePullRequest(baseProject, base, fullHead, title, body) } else if flagPullRequestIssue != "" { pr, err = client.CreatePullRequestForIssue(baseProject, base, fullHead, flagPullRequestIssue) } if err == nil && editor != nil { defer editor.DeleteFile() } utils.Check(err) pullRequestURL = pr.HTMLURL if flagPullRequestAssignee != "" || flagPullRequestMilestone > 0 || flagPullRequestLabels != "" { params := octokit.IssueParams{ Assignee: flagPullRequestAssignee, Milestone: flagPullRequestMilestone, Labels: strings.Split(flagPullRequestLabels, ","), } err = client.UpdateIssue(baseProject, pr.Number, params) utils.Check(err) } } if flagPullRequestBrowse { launcher, err := utils.BrowserLauncher() utils.Check(err) args.Replace(launcher[0], "", launcher[1:]...) args.AppendParams(pullRequestURL) } else { args.Replace("echo", "", pullRequestURL) } if flagPullRequestIssue != "" { args.After("echo", "Warning: Issue to pull request conversion is deprecated and might not work in the future.") } }
func createRelease(cmd *Command, args *Args) { tagName := cmd.Arg(0) if tagName == "" { utils.Check(fmt.Errorf("Missing argument TAG")) return } localRepo, err := github.LocalRepo() utils.Check(err) project, err := localRepo.CurrentProject() utils.Check(err) gh := github.NewClient(project.Host) var title string var body string var editor *github.Editor if cmd.FlagPassed("message") { title, body = readMsg(flagReleaseMessage) } else if cmd.FlagPassed("file") { title, body, editor, err = readMsgFromFile(flagReleaseFile, flagReleaseEdit, "RELEASE", "release") utils.Check(err) } else { cs := git.CommentChar() message, err := renderReleaseTpl("Creating", cs, tagName, project.String(), flagReleaseCommitish) utils.Check(err) editor, err := github.NewEditor("RELEASE", "release", message) utils.Check(err) title, body, err = editor.EditTitleAndBody() utils.Check(err) } if title == "" { utils.Check(fmt.Errorf("Aborting release due to empty release title")) } params := &github.Release{ TagName: tagName, TargetCommitish: flagReleaseCommitish, Name: title, Body: body, Draft: flagReleaseDraft, Prerelease: flagReleasePrerelease, } var release *github.Release args.NoForward() if args.Noop { ui.Printf("Would create release `%s' for %s with tag name `%s'\n", title, project, tagName) } else { release, err = gh.CreateRelease(project, params) utils.Check(err) printBrowseOrCopy(args, release.HtmlUrl, flagReleaseBrowse, flagReleaseCopy) } if editor != nil { editor.DeleteFile() } uploadAssets(gh, release, flagReleaseAssets, args) }
func createIssue(cmd *Command, args *Args) { localRepo, err := github.LocalRepo() utils.Check(err) project, err := localRepo.MainProject() utils.Check(err) gh := github.NewClient(project.Host) var title string var body string var editor *github.Editor if cmd.FlagPassed("message") { title, body = readMsg(flagIssueMessage) } else if cmd.FlagPassed("file") { title, body, err = readMsgFromFile(flagIssueFile) utils.Check(err) } else { cs := git.CommentChar() message := strings.Replace(fmt.Sprintf(` # Creating an issue for %s # # Write a message for this issue. The first block of # text is the title and the rest is the description. `, project), "#", cs, -1) editor, err := github.NewEditor("ISSUE", "issue", message) utils.Check(err) title, body, err = editor.EditTitleAndBody() utils.Check(err) } if title == "" { utils.Check(fmt.Errorf("Aborting creation due to empty issue title")) } params := map[string]interface{}{ "title": title, "body": body, "labels": flagIssueLabels, "assignees": flagIssueAssignees, } if flagIssueMilestone > 0 { params["milestone"] = flagIssueMilestone } if args.Noop { ui.Printf("Would create issue `%s' for %s\n", params["title"], project) os.Exit(0) } else { issue, err := gh.CreateIssue(project, params) utils.Check(err) if editor != nil { editor.DeleteFile() } if flagIssueBrowse { launcher, err := utils.BrowserLauncher() utils.Check(err) args.Replace(launcher[0], "", launcher[1:]...) args.AppendParams(issue.HtmlUrl) } else { ui.Println(issue.HtmlUrl) os.Exit(0) } } }
func pullRequest(cmd *Command, args *Args) { localRepo, err := github.LocalRepo() utils.Check(err) currentBranch, err := localRepo.CurrentBranch() utils.Check(err) baseProject, err := localRepo.MainProject() utils.Check(err) host, err := github.CurrentConfig().PromptForHost(baseProject.Host) if err != nil { utils.Check(github.FormatError("creating pull request", err)) } client := github.NewClientWithHost(host) trackedBranch, headProject, err := localRepo.RemoteBranchAndProject(host.User, false) utils.Check(err) var ( base, head string force bool ) force = flagPullRequestForce if flagPullRequestBase != "" { baseProject, base = parsePullRequestProject(baseProject, flagPullRequestBase) } if flagPullRequestHead != "" { headProject, head = parsePullRequestProject(headProject, flagPullRequestHead) } if args.ParamsSize() == 1 { arg := args.RemoveParam(0) flagPullRequestIssue = parsePullRequestIssueNumber(arg) } if base == "" { masterBranch := localRepo.MasterBranch() base = masterBranch.ShortName() } if head == "" && trackedBranch != nil { if !trackedBranch.IsRemote() { // the current branch tracking another branch // pretend there's no upstream at all trackedBranch = nil } else { if baseProject.SameAs(headProject) && base == trackedBranch.ShortName() { e := fmt.Errorf(`Aborted: head branch is the same as base ("%s")`, base) e = fmt.Errorf("%s\n(use `-h <branch>` to specify an explicit pull request head)", e) utils.Check(e) } } } if head == "" { if trackedBranch == nil { head = currentBranch.ShortName() } else { head = trackedBranch.ShortName() } } if headRepo, err := client.Repository(headProject); err == nil { headProject.Owner = headRepo.Owner.Login headProject.Name = headRepo.Name } fullBase := fmt.Sprintf("%s:%s", baseProject.Owner, base) fullHead := fmt.Sprintf("%s:%s", headProject.Owner, head) if !force && trackedBranch != nil { remoteCommits, _ := git.RefList(trackedBranch.LongName(), "") if len(remoteCommits) > 0 { err = fmt.Errorf("Aborted: %d commits are not yet pushed to %s", len(remoteCommits), trackedBranch.LongName()) err = fmt.Errorf("%s\n(use `-f` to force submit a pull request anyway)", err) utils.Check(err) } } var editor *github.Editor var title, body string baseTracking := base headTracking := head remote := gitRemoteForProject(baseProject) if remote != nil { baseTracking = fmt.Sprintf("%s/%s", remote.Name, base) } if remote == nil || !baseProject.SameAs(headProject) { remote = gitRemoteForProject(headProject) } if remote != nil { headTracking = fmt.Sprintf("%s/%s", remote.Name, head) } if flagPullRequestPush && remote == nil { utils.Check(fmt.Errorf("Can't find remote for %s", head)) } if cmd.FlagPassed("message") { title, body = readMsg(flagPullRequestMessage) } else if cmd.FlagPassed("file") { title, body, editor, err = readMsgFromFile(flagPullRequestFile, flagPullRequestEdit, "PULLREQ", "pull request") utils.Check(err) } else if flagPullRequestIssue == "" { headForMessage := headTracking if flagPullRequestPush { headForMessage = head } message, err := createPullRequestMessage(baseTracking, headForMessage, fullBase, fullHead) utils.Check(err) editor, err = github.NewEditor("PULLREQ", "pull request", message) utils.Check(err) title, body, err = editor.EditTitleAndBody() utils.Check(err) } if title == "" && flagPullRequestIssue == "" { utils.Check(fmt.Errorf("Aborting due to empty pull request title")) } if flagPullRequestPush { if args.Noop { args.Before(fmt.Sprintf("Would push to %s/%s", remote.Name, head), "") } else { err = git.Spawn("push", "--set-upstream", remote.Name, fmt.Sprintf("HEAD:%s", head)) utils.Check(err) } } var pullRequestURL string if args.Noop { args.Before(fmt.Sprintf("Would request a pull request to %s from %s", fullBase, fullHead), "") pullRequestURL = "PULL_REQUEST_URL" } else { params := map[string]interface{}{ "base": base, "head": fullHead, } if title != "" { params["title"] = title if body != "" { params["body"] = body } } else { issueNum, _ := strconv.Atoi(flagPullRequestIssue) params["issue"] = issueNum } startedAt := time.Now() numRetries := 0 retryDelay := 2 retryAllowance := 0 if flagPullRequestPush { if allowanceFromEnv := os.Getenv("HUB_RETRY_TIMEOUT"); allowanceFromEnv != "" { retryAllowance, err = strconv.Atoi(allowanceFromEnv) utils.Check(err) } else { retryAllowance = 9 } } var pr *github.PullRequest for { pr, err = client.CreatePullRequest(baseProject, params) if err != nil && strings.Contains(err.Error(), `Invalid value for "head"`) { if retryAllowance > 0 { retryAllowance -= retryDelay time.Sleep(time.Duration(retryDelay) * time.Second) retryDelay += 1 numRetries += 1 } else { if numRetries > 0 { duration := time.Now().Sub(startedAt) err = fmt.Errorf("%s\nGiven up after retrying for %.1f seconds.", err, duration.Seconds()) } break } } else { break } } if err == nil && editor != nil { defer editor.DeleteFile() } utils.Check(err) pullRequestURL = pr.HtmlUrl params = map[string]interface{}{} if len(flagPullRequestLabels) > 0 { params["labels"] = flagPullRequestLabels } if len(flagPullRequestAssignees) > 0 { params["assignees"] = flagPullRequestAssignees } if flagPullRequestMilestone > 0 { params["milestone"] = flagPullRequestMilestone } if len(params) > 0 { err = client.UpdateIssue(baseProject, pr.Number, params) utils.Check(err) } } if flagPullRequestIssue != "" { ui.Errorln("Warning: Issue to pull request conversion is deprecated and might not work in the future.") } args.NoForward() printBrowseOrCopy(args, pullRequestURL, flagPullRequestBrowse, flagPullRequestCopy) }
func createRelease(cmd *Command, args *Args) { if args.IsParamsEmpty() { utils.Check(fmt.Errorf("Missed argument TAG")) return } tag := args.LastParam() runInLocalRepo(func(localRepo *github.GitHubRepo, project *github.Project, client *github.Client) { release, err := client.Release(project, tag) utils.Check(err) if release == nil { commitish := flagReleaseCommitish if commitish == "" { currentBranch, err := localRepo.CurrentBranch() utils.Check(err) commitish = currentBranch.ShortName() } title, body, err := getTitleAndBodyFromFlags(flagReleaseMessage, flagReleaseFile) utils.Check(err) var editor *github.Editor if title == "" { cs := git.CommentChar() message, err := renderReleaseTpl(cs, tag, project.Name, commitish) utils.Check(err) editor, err = github.NewEditor("RELEASE", "release", message) utils.Check(err) title, body, err = editor.EditTitleAndBody() utils.Check(err) } params := octokit.ReleaseParams{ TagName: tag, TargetCommitish: commitish, Name: title, Body: body, Draft: flagReleaseDraft, Prerelease: flagReleasePrerelease, } release, err = client.CreateRelease(project, params) utils.Check(err) if editor != nil { defer editor.DeleteFile() } } if len(flagReleaseAssets) > 0 { paths := make([]string, 0) for _, asset := range flagReleaseAssets { finder := assetFinder{} p, err := finder.Find(asset) utils.Check(err) paths = append(paths, p...) } uploader := assetUploader{ Client: client, Release: release, } err = uploader.UploadAll(paths) if err != nil { ui.Println("") utils.Check(err) } } ui.Printf("\n%s\n", release.HTMLURL) }) }
func createIssue(cmd *Command, args *Args) { localRepo, err := github.LocalRepo() utils.Check(err) project, err := localRepo.MainProject() utils.Check(err) gh := github.NewClient(project.Host) var title string var body string var editor *github.Editor if cmd.FlagPassed("message") { title, body = readMsg(flagIssueMessage) } else if cmd.FlagPassed("file") { title, body, editor, err = readMsgFromFile(flagIssueFile, flagIssueEdit, "ISSUE", "issue") utils.Check(err) } else { cs := git.CommentChar() message := strings.Replace(fmt.Sprintf(` # Creating an issue for %s # # Write a message for this issue. The first block of # text is the title and the rest is the description. `, project), "#", cs, -1) workdir, err := git.WorkdirName() utils.Check(err) template, err := github.ReadTemplate(github.IssueTemplate, workdir) utils.Check(err) if template != "" { message = template + "\n" + message } editor, err := github.NewEditor("ISSUE", "issue", message) utils.Check(err) title, body, err = editor.EditTitleAndBody() utils.Check(err) } if editor != nil { defer editor.DeleteFile() } if title == "" { utils.Check(fmt.Errorf("Aborting creation due to empty issue title")) } params := map[string]interface{}{ "title": title, "body": body, } if len(flagIssueLabels) > 0 { params["labels"] = flagIssueLabels } if len(flagIssueAssignees) > 0 { params["assignees"] = flagIssueAssignees } if flagIssueMilestone > 0 { params["milestone"] = flagIssueMilestone } args.NoForward() if args.Noop { ui.Printf("Would create issue `%s' for %s\n", params["title"], project) } else { issue, err := gh.CreateIssue(project, params) utils.Check(err) printBrowseOrCopy(args, issue.HtmlUrl, flagIssueBrowse, flagIssueCopy) } }