gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/tools/github/main.go (about)

     1  // Copyright 2019 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Binary github is the entry point for GitHub utilities.
    16  package main
    17  
    18  import (
    19  	"context"
    20  	"flag"
    21  	"fmt"
    22  	"io/ioutil"
    23  	"log"
    24  	"os"
    25  	"strings"
    26  
    27  	"github.com/google/go-github/github"
    28  	"golang.org/x/oauth2"
    29  	"gvisor.dev/gvisor/tools/github/reviver"
    30  )
    31  
    32  var (
    33  	owner     string
    34  	repo      string
    35  	tokenFile string
    36  	paths     stringList
    37  	commit    string
    38  	dryRun    bool
    39  )
    40  
    41  type stringList []string
    42  
    43  func (s *stringList) String() string {
    44  	return strings.Join(*s, ",")
    45  }
    46  
    47  func (s *stringList) Set(value string) error {
    48  	*s = append(*s, value)
    49  	return nil
    50  }
    51  
    52  // Keep the options simple for now. Supports only a single path and repo.
    53  func init() {
    54  	flag.StringVar(&owner, "owner", "", "GitHub project org/owner")
    55  	flag.StringVar(&repo, "repo", "", "GitHub repo")
    56  	flag.StringVar(&tokenFile, "oauth-token-file", "", "file containing the GitHub token (or GITHUB_TOKEN is set)")
    57  	flag.Var(&paths, "path", "path(s) to scan (required for revive)")
    58  	flag.BoolVar(&dryRun, "dry-run", false, "just print changes to be made")
    59  }
    60  
    61  func filterPaths(paths []string) (existing []string) {
    62  	for _, path := range paths {
    63  		if _, err := os.Stat(path); err != nil {
    64  			log.Printf("WARNING: skipping %v: %v", path, err)
    65  			continue
    66  		}
    67  		existing = append(existing, path)
    68  	}
    69  	return
    70  }
    71  
    72  func main() {
    73  	// Set defaults from the environment.
    74  	repository := os.Getenv("GITHUB_REPOSITORY")
    75  	if parts := strings.SplitN(repository, "/", 2); len(parts) == 2 {
    76  		owner = parts[0]
    77  		repo = parts[1]
    78  	}
    79  
    80  	// Parse flags.
    81  	flag.Usage = func() {
    82  		fmt.Fprintf(flag.CommandLine.Output(), "usage: %s [options] <command>\n", os.Args[0])
    83  		fmt.Fprintf(flag.CommandLine.Output(), "commands: revive, nogo\n")
    84  		flag.PrintDefaults()
    85  	}
    86  	flag.Parse()
    87  	args := flag.Args()
    88  	if len(args) != 1 {
    89  		fmt.Fprintf(flag.CommandLine.Output(), "extra arguments: %s\n", strings.Join(args[1:], ", "))
    90  		flag.Usage()
    91  		os.Exit(1)
    92  	}
    93  
    94  	// Check for mandatory parameters.
    95  	command := args[0]
    96  	if len(owner) == 0 {
    97  		fmt.Fprintln(flag.CommandLine.Output(), "missing --owner option.")
    98  		flag.Usage()
    99  		os.Exit(1)
   100  	}
   101  	if len(repo) == 0 {
   102  		fmt.Fprintln(flag.CommandLine.Output(), "missing --repo option.")
   103  		flag.Usage()
   104  		os.Exit(1)
   105  	}
   106  	filteredPaths := filterPaths(paths)
   107  	if len(filteredPaths) == 0 {
   108  		fmt.Fprintln(flag.CommandLine.Output(), "no valid --path options provided.")
   109  		flag.Usage()
   110  		os.Exit(1)
   111  	}
   112  
   113  	// The access token may be passed as a file so it doesn't show up in
   114  	// command line arguments. It also may be provided through the
   115  	// environment to faciliate use through GitHub's CI system.
   116  	token := os.Getenv("GITHUB_TOKEN")
   117  	if len(tokenFile) != 0 {
   118  		bytes, err := ioutil.ReadFile(tokenFile)
   119  		if err != nil {
   120  			fmt.Println(err.Error())
   121  			os.Exit(1)
   122  		}
   123  		token = string(bytes)
   124  	}
   125  	var client *github.Client
   126  	if len(token) == 0 {
   127  		// Client is unauthenticated.
   128  		client = github.NewClient(nil)
   129  	} else {
   130  		// Using the above token.
   131  		ts := oauth2.StaticTokenSource(
   132  			&oauth2.Token{AccessToken: token},
   133  		)
   134  		tc := oauth2.NewClient(context.Background(), ts)
   135  		client = github.NewClient(tc)
   136  	}
   137  
   138  	switch command {
   139  	case "revive":
   140  		// Load existing GitHub bugs.
   141  		bugger, err := reviver.NewGitHubBugger(client, owner, repo, dryRun)
   142  		if err != nil {
   143  			fmt.Fprintf(os.Stderr, "Error getting github issues: %v\n", err)
   144  			os.Exit(1)
   145  		}
   146  		// Scan the provided path.
   147  		rev := reviver.New(filteredPaths, []reviver.Bugger{bugger})
   148  		if errs := rev.Run(); len(errs) > 0 {
   149  			fmt.Fprintf(os.Stderr, "Encountered %d errors:\n", len(errs))
   150  			for _, err := range errs {
   151  				fmt.Fprintf(os.Stderr, "\t%v\n", err)
   152  			}
   153  			os.Exit(1)
   154  		}
   155  	default:
   156  		// Not a known command.
   157  		fmt.Fprintf(flag.CommandLine.Output(), "unknown command: %s\n", command)
   158  		flag.Usage()
   159  		os.Exit(1)
   160  	}
   161  }