1// Copyright 2021 The Bazel Authors. All rights reserved. 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// releaser is a tool for maintaining rules_go and Gazelle. It automates 16// multiple tasks related to preparing releases, like upgrading dependencies, 17// and uploading release artifacts. 18package main 19 20import ( 21 "context" 22 "errors" 23 "fmt" 24 "io" 25 "os" 26 "os/signal" 27) 28 29func main() { 30 ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) 31 defer cancel() 32 if err := run(ctx, os.Stderr, os.Args[1:]); err != nil { 33 fmt.Fprintln(os.Stderr, err) 34 os.Exit(1) 35 } 36} 37 38type command struct { 39 name, description, help string 40 run func(context.Context, io.Writer, []string) error 41} 42 43var commands = []*command{ 44 &helpCmd, 45 &prepareCmd, 46 &upgradeDepCmd, 47} 48 49func run(ctx context.Context, stderr io.Writer, args []string) error { 50 if len(args) == 0 { 51 return errors.New("no command specified. For a list of commands, run:\n\treleaser help") 52 } 53 name, args := args[0], args[1:] 54 for _, arg := range args { 55 if arg == "-h" || name == "-help" || name == "--help" { 56 return helpCmd.run(ctx, stderr, args) 57 } 58 } 59 for _, cmd := range commands { 60 if cmd.name == name { 61 return cmd.run(ctx, stderr, args) 62 } 63 } 64 return fmt.Errorf("unknown command %q. For a list of commands, run:\n\treleaser help", name) 65} 66 67var helpCmd = command{ 68 name: "help", 69 description: "prints information on how to use any subcommand", 70 help: `releaser help [subcommand] 71 72The help sub-command prints information on how to use any subcommand. Run help 73without arguments for a list of all subcommands. 74`, 75} 76 77func init() { 78 // break init cycle 79 helpCmd.run = runHelp 80} 81 82func runHelp(ctx context.Context, stderr io.Writer, args []string) error { 83 if len(args) > 1 { 84 return usageErrorf(&helpCmd, "help accepts at most one argument.") 85 } 86 87 if len(args) == 1 { 88 name := args[0] 89 for _, cmd := range commands { 90 if cmd.name == name { 91 fmt.Fprintf(stderr, "%s\n\n%s\n", cmd.description, cmd.help) 92 return nil 93 } 94 } 95 return fmt.Errorf("Unknown command %s. For a list of supported subcommands, run:\n\treleaser help", name) 96 } 97 98 fmt.Fprint(stderr, "releaser supports the following subcommands:\n\n") 99 maxNameLen := 0 100 for _, cmd := range commands { 101 if len(cmd.name) > maxNameLen { 102 maxNameLen = len(cmd.name) 103 } 104 } 105 for _, cmd := range commands { 106 fmt.Fprintf(stderr, "\t%-*s %s\n", maxNameLen, cmd.name, cmd.description) 107 } 108 fmt.Fprintf(stderr, "\nRun 'releaser help <subcommand>' for more information on any command.\n") 109 return nil 110} 111 112type usageError struct { 113 cmd *command 114 err error 115} 116 117func (e *usageError) Error() string { 118 return fmt.Sprintf("%v. For usage info, run:\n\treleaser help %s", e.err, e.cmd.name) 119} 120 121func (e *usageError) Unwrap() error { 122 return e.err 123} 124 125func usageErrorf(cmd *command, format string, args ...interface{}) error { 126 return &usageError{cmd: cmd, err: fmt.Errorf(format, args...)} 127} 128