...

Source file src/github.com/google/go-github/v33/example/commitpr/main.go

Documentation: github.com/google/go-github/v33/example/commitpr

     1  // Copyright 2018 The go-github AUTHORS. All rights reserved.
     2  //
     3  // Use of this source code is governed by a BSD-style
     4  // license that can be found in the LICENSE file.
     5  
     6  // The commitpr command utilizes go-github as a CLI tool for
     7  // pushing files to a branch and creating a pull request from it.
     8  // It takes an auth token as an environment variable and creates
     9  // the commit and the PR under the account affiliated with that token.
    10  //
    11  // The purpose of this example is to show how to use refs, trees and commits to
    12  // create commits and pull requests.
    13  //
    14  // Note, if you want to push a single file, you probably prefer to use the
    15  // content API. An example is available here:
    16  // https://godoc.org/github.com/google/go-github/github#example-RepositoriesService-CreateFile
    17  //
    18  // Note, for this to work at least 1 commit is needed, so you if you use this
    19  // after creating a repository you might want to make sure you set `AutoInit` to
    20  // `true`.
    21  package main
    22  
    23  import (
    24  	"context"
    25  	"errors"
    26  	"flag"
    27  	"fmt"
    28  	"io/ioutil"
    29  	"log"
    30  	"os"
    31  	"strings"
    32  	"time"
    33  
    34  	"github.com/google/go-github/v33/github"
    35  	"golang.org/x/oauth2"
    36  )
    37  
    38  var (
    39  	sourceOwner   = flag.String("source-owner", "", "Name of the owner (user or org) of the repo to create the commit in.")
    40  	sourceRepo    = flag.String("source-repo", "", "Name of repo to create the commit in.")
    41  	commitMessage = flag.String("commit-message", "", "Content of the commit message.")
    42  	commitBranch  = flag.String("commit-branch", "", "Name of branch to create the commit in. If it does not already exists, it will be created using the `base-branch` parameter")
    43  	baseBranch    = flag.String("base-branch", "master", "Name of branch to create the `commit-branch` from.")
    44  	prRepoOwner   = flag.String("merge-repo-owner", "", "Name of the owner (user or org) of the repo to create the PR against. If not specified, the value of the `-source-owner` flag will be used.")
    45  	prRepo        = flag.String("merge-repo", "", "Name of repo to create the PR against. If not specified, the value of the `-source-repo` flag will be used.")
    46  	prBranch      = flag.String("merge-branch", "master", "Name of branch to create the PR against (the one you want to merge your branch in via the PR).")
    47  	prSubject     = flag.String("pr-title", "", "Title of the pull request. If not specified, no pull request will be created.")
    48  	prDescription = flag.String("pr-text", "", "Text to put in the description of the pull request.")
    49  	sourceFiles   = flag.String("files", "", `Comma-separated list of files to commit and their location.
    50  The local file is separated by its target location by a semi-colon.
    51  If the file should be in the same location with the same name, you can just put the file name and omit the repetition.
    52  Example: README.md,main.go:github/examples/commitpr/main.go`)
    53  	authorName  = flag.String("author-name", "", "Name of the author of the commit.")
    54  	authorEmail = flag.String("author-email", "", "Email of the author of the commit.")
    55  )
    56  
    57  var client *github.Client
    58  var ctx = context.Background()
    59  
    60  // getRef returns the commit branch reference object if it exists or creates it
    61  // from the base branch before returning it.
    62  func getRef() (ref *github.Reference, err error) {
    63  	if ref, _, err = client.Git.GetRef(ctx, *sourceOwner, *sourceRepo, "refs/heads/"+*commitBranch); err == nil {
    64  		return ref, nil
    65  	}
    66  
    67  	// We consider that an error means the branch has not been found and needs to
    68  	// be created.
    69  	if *commitBranch == *baseBranch {
    70  		return nil, errors.New("The commit branch does not exist but `-base-branch` is the same as `-commit-branch`")
    71  	}
    72  
    73  	if *baseBranch == "" {
    74  		return nil, errors.New("The `-base-branch` should not be set to an empty string when the branch specified by `-commit-branch` does not exists")
    75  	}
    76  
    77  	var baseRef *github.Reference
    78  	if baseRef, _, err = client.Git.GetRef(ctx, *sourceOwner, *sourceRepo, "refs/heads/"+*baseBranch); err != nil {
    79  		return nil, err
    80  	}
    81  	newRef := &github.Reference{Ref: github.String("refs/heads/" + *commitBranch), Object: &github.GitObject{SHA: baseRef.Object.SHA}}
    82  	ref, _, err = client.Git.CreateRef(ctx, *sourceOwner, *sourceRepo, newRef)
    83  	return ref, err
    84  }
    85  
    86  // getTree generates the tree to commit based on the given files and the commit
    87  // of the ref you got in getRef.
    88  func getTree(ref *github.Reference) (tree *github.Tree, err error) {
    89  	// Create a tree with what to commit.
    90  	entries := []*github.TreeEntry{}
    91  
    92  	// Load each file into the tree.
    93  	for _, fileArg := range strings.Split(*sourceFiles, ",") {
    94  		file, content, err := getFileContent(fileArg)
    95  		if err != nil {
    96  			return nil, err
    97  		}
    98  		entries = append(entries, &github.TreeEntry{Path: github.String(file), Type: github.String("blob"), Content: github.String(string(content)), Mode: github.String("100644")})
    99  	}
   100  
   101  	tree, _, err = client.Git.CreateTree(ctx, *sourceOwner, *sourceRepo, *ref.Object.SHA, entries)
   102  	return tree, err
   103  }
   104  
   105  // getFileContent loads the local content of a file and return the target name
   106  // of the file in the target repository and its contents.
   107  func getFileContent(fileArg string) (targetName string, b []byte, err error) {
   108  	var localFile string
   109  	files := strings.Split(fileArg, ":")
   110  	switch {
   111  	case len(files) < 1:
   112  		return "", nil, errors.New("empty `-files` parameter")
   113  	case len(files) == 1:
   114  		localFile = files[0]
   115  		targetName = files[0]
   116  	default:
   117  		localFile = files[0]
   118  		targetName = files[1]
   119  	}
   120  
   121  	b, err = ioutil.ReadFile(localFile)
   122  	return targetName, b, err
   123  }
   124  
   125  // pushCommit creates the commit in the given reference using the given tree.
   126  func pushCommit(ref *github.Reference, tree *github.Tree) (err error) {
   127  	// Get the parent commit to attach the commit to.
   128  	parent, _, err := client.Repositories.GetCommit(ctx, *sourceOwner, *sourceRepo, *ref.Object.SHA)
   129  	if err != nil {
   130  		return err
   131  	}
   132  	// This is not always populated, but is needed.
   133  	parent.Commit.SHA = parent.SHA
   134  
   135  	// Create the commit using the tree.
   136  	date := time.Now()
   137  	author := &github.CommitAuthor{Date: &date, Name: authorName, Email: authorEmail}
   138  	commit := &github.Commit{Author: author, Message: commitMessage, Tree: tree, Parents: []*github.Commit{parent.Commit}}
   139  	newCommit, _, err := client.Git.CreateCommit(ctx, *sourceOwner, *sourceRepo, commit)
   140  	if err != nil {
   141  		return err
   142  	}
   143  
   144  	// Attach the commit to the master branch.
   145  	ref.Object.SHA = newCommit.SHA
   146  	_, _, err = client.Git.UpdateRef(ctx, *sourceOwner, *sourceRepo, ref, false)
   147  	return err
   148  }
   149  
   150  // createPR creates a pull request. Based on: https://godoc.org/github.com/google/go-github/github#example-PullRequestsService-Create
   151  func createPR() (err error) {
   152  	if *prSubject == "" {
   153  		return errors.New("missing `-pr-title` flag; skipping PR creation")
   154  	}
   155  
   156  	if *prRepoOwner != "" && *prRepoOwner != *sourceOwner {
   157  		*commitBranch = fmt.Sprintf("%s:%s", *sourceOwner, *commitBranch)
   158  	} else {
   159  		prRepoOwner = sourceOwner
   160  	}
   161  
   162  	if *prRepo == "" {
   163  		prRepo = sourceRepo
   164  	}
   165  
   166  	newPR := &github.NewPullRequest{
   167  		Title:               prSubject,
   168  		Head:                commitBranch,
   169  		Base:                prBranch,
   170  		Body:                prDescription,
   171  		MaintainerCanModify: github.Bool(true),
   172  	}
   173  
   174  	pr, _, err := client.PullRequests.Create(ctx, *prRepoOwner, *prRepo, newPR)
   175  	if err != nil {
   176  		return err
   177  	}
   178  
   179  	fmt.Printf("PR created: %s\n", pr.GetHTMLURL())
   180  	return nil
   181  }
   182  
   183  func main() {
   184  	flag.Parse()
   185  	token := os.Getenv("GITHUB_AUTH_TOKEN")
   186  	if token == "" {
   187  		log.Fatal("Unauthorized: No token present")
   188  	}
   189  	if *sourceOwner == "" || *sourceRepo == "" || *commitBranch == "" || *sourceFiles == "" || *authorName == "" || *authorEmail == "" {
   190  		log.Fatal("You need to specify a non-empty value for the flags `-source-owner`, `-source-repo`, `-commit-branch`, `-files`, `-author-name` and `-author-email`")
   191  	}
   192  	ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})
   193  	tc := oauth2.NewClient(ctx, ts)
   194  	client = github.NewClient(tc)
   195  
   196  	ref, err := getRef()
   197  	if err != nil {
   198  		log.Fatalf("Unable to get/create the commit reference: %s\n", err)
   199  	}
   200  	if ref == nil {
   201  		log.Fatalf("No error where returned but the reference is nil")
   202  	}
   203  
   204  	tree, err := getTree(ref)
   205  	if err != nil {
   206  		log.Fatalf("Unable to create the tree based on the provided files: %s\n", err)
   207  	}
   208  
   209  	if err := pushCommit(ref, tree); err != nil {
   210  		log.Fatalf("Unable to create the commit: %s\n", err)
   211  	}
   212  
   213  	if err := createPR(); err != nil {
   214  		log.Fatalf("Error while creating the pull request: %s", err)
   215  	}
   216  }
   217  

View as plain text