|
| 1 | +package cmd |
| 2 | + |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + "encoding/json" |
| 6 | + "fmt" |
| 7 | + "net/url" |
| 8 | + "os" |
| 9 | + "os/signal" |
| 10 | + "path" |
| 11 | + "syscall" |
| 12 | + "time" |
| 13 | + |
| 14 | + "github.com/bufbuild/connect-go" |
| 15 | + "github.com/google/uuid" |
| 16 | + "github.com/overmindtech/ovm-cli/tracing" |
| 17 | + "github.com/overmindtech/sdp-go" |
| 18 | + log "github.com/sirupsen/logrus" |
| 19 | + "github.com/spf13/cobra" |
| 20 | + "github.com/spf13/viper" |
| 21 | + "go.opentelemetry.io/otel/attribute" |
| 22 | + "go.opentelemetry.io/otel/trace" |
| 23 | +) |
| 24 | + |
| 25 | +// getChangeCmd represents the get-change command |
| 26 | +var getChangeCmd = &cobra.Command{ |
| 27 | + Use: "get-change {--uuid ID | --change https://app.overmind.tech/changes/c772d072-6b0b-4763-b7c5-ff5069beed4c}", |
| 28 | + Short: "Displays the contents of a change.", |
| 29 | + PreRun: func(cmd *cobra.Command, args []string) { |
| 30 | + // Bind these to viper |
| 31 | + err := viper.BindPFlags(cmd.Flags()) |
| 32 | + if err != nil { |
| 33 | + log.WithError(err).Fatal("could not bind `get-change` flags") |
| 34 | + } |
| 35 | + }, |
| 36 | + Run: func(cmd *cobra.Command, args []string) { |
| 37 | + sigs := make(chan os.Signal, 1) |
| 38 | + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) |
| 39 | + |
| 40 | + exitcode := GetChange(sigs, nil) |
| 41 | + tracing.ShutdownTracer() |
| 42 | + os.Exit(exitcode) |
| 43 | + }, |
| 44 | +} |
| 45 | + |
| 46 | +func GetChange(signals chan os.Signal, ready chan bool) int { |
| 47 | + timeout, err := time.ParseDuration(viper.GetString("timeout")) |
| 48 | + if err != nil { |
| 49 | + log.Errorf("invalid --timeout value '%v', error: %v", viper.GetString("timeout"), err) |
| 50 | + return 1 |
| 51 | + } |
| 52 | + |
| 53 | + var changeUuid uuid.UUID |
| 54 | + if viper.GetString("uuid") != "" { |
| 55 | + changeUuid, err = uuid.Parse(viper.GetString("uuid")) |
| 56 | + if err != nil { |
| 57 | + log.Errorf("invalid --uuid value '%v', error: %v", viper.GetString("uuid"), err) |
| 58 | + return 1 |
| 59 | + } |
| 60 | + } |
| 61 | + |
| 62 | + if viper.GetString("change") != "" { |
| 63 | + changeUrl, err := url.ParseRequestURI(viper.GetString("change")) |
| 64 | + if err != nil { |
| 65 | + log.Errorf("invalid --change value '%v', error: %v", viper.GetString("change"), err) |
| 66 | + return 1 |
| 67 | + } |
| 68 | + changeUuid, err = uuid.Parse(path.Base(changeUrl.Path)) |
| 69 | + if err != nil { |
| 70 | + log.Errorf("invalid --change value '%v', couldn't parse: %v", viper.GetString("change"), err) |
| 71 | + return 1 |
| 72 | + } |
| 73 | + } |
| 74 | + |
| 75 | + if changeUuid == uuid.Nil { |
| 76 | + log.Error("no change specified; use one of --uuid or --change") |
| 77 | + return 1 |
| 78 | + } |
| 79 | + |
| 80 | + ctx := context.Background() |
| 81 | + ctx, span := tracing.Tracer().Start(ctx, "CLI GetChange", trace.WithAttributes( |
| 82 | + attribute.String("om.config", fmt.Sprintf("%v", viper.AllSettings())), |
| 83 | + )) |
| 84 | + defer span.End() |
| 85 | + |
| 86 | + ctx, err = ensureToken(ctx, signals) |
| 87 | + if err != nil { |
| 88 | + log.WithContext(ctx).WithError(err).WithFields(log.Fields{ |
| 89 | + "url": viper.GetString("url"), |
| 90 | + }).Error("failed to authenticate") |
| 91 | + return 1 |
| 92 | + } |
| 93 | + |
| 94 | + // apply a timeout to the main body of processing |
| 95 | + ctx, cancel := context.WithTimeout(ctx, timeout) |
| 96 | + defer cancel() |
| 97 | + |
| 98 | + client := AuthenticatedChangesClient(ctx) |
| 99 | + response, err := client.GetChange(ctx, &connect.Request[sdp.GetChangeRequest]{ |
| 100 | + Msg: &sdp.GetChangeRequest{ |
| 101 | + UUID: changeUuid[:], |
| 102 | + }, |
| 103 | + }) |
| 104 | + if err != nil { |
| 105 | + log.WithContext(ctx).WithError(err).WithFields(log.Fields{ |
| 106 | + "change-url": viper.GetString("change-url"), |
| 107 | + }).Error("failed to get change") |
| 108 | + return 1 |
| 109 | + } |
| 110 | + log.WithContext(ctx).WithFields(log.Fields{ |
| 111 | + "change-uuid": uuid.UUID(response.Msg.Change.Metadata.UUID), |
| 112 | + "change-created": response.Msg.Change.Metadata.CreatedAt.AsTime(), |
| 113 | + "change-name": response.Msg.Change.Properties.Title, |
| 114 | + "change-description": response.Msg.Change.Properties.Description, |
| 115 | + }).Info("found change") |
| 116 | + |
| 117 | + switch viper.GetString("format") { |
| 118 | + case "json": |
| 119 | + b, _ := json.MarshalIndent(response.Msg.Change, "", " ") |
| 120 | + fmt.Println(string(b)) |
| 121 | + case "markdown": |
| 122 | + changeUrl := fmt.Sprintf("%v/changes/%v", viper.GetString("frontend"), changeUuid.String()) |
| 123 | + if response.Msg.Change.Metadata.NumAffectedApps != 0 || response.Msg.Change.Metadata.NumAffectedItems != 0 { |
| 124 | + // we have affected stuff |
| 125 | + fmt.Printf(`## Blast Radius · [View in Overmind](%v) <img align="center" width="16" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAPwSURBVHgB7VrdVdswGFVCBkg3cDdIJyBMUHyAZ5INyASlE6SdAPPKX+gENRPABmSDZgBIem9ipZ8VKZaR7fRwuOfk2LJk+V79fLoSKPWB3aKl/gNMJpMuLvzN4jielXl3ZwJAuo/L6Xw+5zUSWRSQ4vfr6OgoKaqncQEg3lssFmP8+h7Fpyg3Oj4+vncVaKsGcXd3xxb/7UmeiFqtFjRPvrkKNCaA5EE8UauxrpFA0EG73f6E4dLilWmUu5Tv4tm5S0QjQ0iQ15iCLOZr/OR6B4Qj9pYS8wPvHOCdVJarvQcc5A+2kSeQvyzH8voZBF2Y5WoVsIX81Of9TEQsHkU3NzeHskxtAkLJa7CnjDkxkPm1CKiKvIasC1FpX+ZVLqBq8kSn05HzpZut3EtUKqAO8oTFXqwFVBZG6yJPsMURgf7oNNcMfV9JD/iQl91eFi8vLz1Zt8wLFsAFB+R/yA+Y5BH6HtmCt7e3tAWRKgnUdyqSaS5PBQLExurfmLS1PP2MbsFDlH9kj3lWrzLBA51G/TmbESSAzpKkROUbY55pkP4uHnU53HxECDuxBN57qtRKoMKBSCauCXtycnIOcZ+VGL8cdtuGk8ULzfb29mKzXJAAfGC9qJhda8LibdgTF7ayNiMHjGwNFCQAhCKRfCoqb3ob7guyndkaDvJD1+4sdAh1BTmvvazpbV5fX3PmrAx5wksAJxxC4DN+Y/kc0WVNukycN9aMfVEHg0Ik8oZF++JCAWKRivA7kxMPrTUVRXvKE9LboBEifZ/1zk+1midDn019Z1umbYWVEwnj+QH5S+IQw7CYKg9wuKE3dTLXc9jAn+Fypjzh7AGXPZBlMH7lacHg6urKqxeM4VbqHMiEVYCvMUN8TzEEUp1GnPayCtLb4DtTFYANAZm3ScSjra4SAoZiMi9DYJEIw9s8qABsCJBLt/KwxMzj4ZN4RBHPrmMQ09ug1xIVgNx+4Pr6ug/CawFc/n39PCblAD0xlmsDz3nk+mBZpO4RaWIVgFwPGF2blNmMMORBwBeSYppzo4D8DN8bqUCYYbSvb4q8jQ2Z4JhRpoA8Mapit2bOgUjcF3obFzzIey1SPmjLD7lIvBV1kydkD8yMj795D5u9Xzt5Yi2ALS7NmSrhbUw0RZ7IzQGEwFTfZ96mNJokT5gCcmeQvt5Go2nyxMbBFn2/IOB9OLUL8oTNSgxF0svbcCOyC/KE9WgRloKnCKaXSbLFjUcbsyxK9bK5MjDKNkKecJ6NOkQUgVFs1BR5YuvhLv8aQoOm8kPDXhG8D611FfagDLxOp+k0cfmqVl5JLnCc5Ck2KJfc3KgdoPTxesi/BXzgPeIvmnKmx43NP2kAAAAASUVORK5CYII=" alt="chain link icon" /> |
| 126 | +
|
| 127 | +> **Warning** |
| 128 | +> Overmind identified potentially affected apps and items as a result of this pull request. |
| 129 | +
|
| 130 | +<br> |
| 131 | +
|
| 132 | +| <img align="center" width="16" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAM0SURBVHgB7ZqxUhsxEIb/FZg0eQB6eAB6aJwUYVKEZMBFKmjyAFR5hlRp0qUxbQ4GSJEJk0zcmN4PEHo/ABUM2uyeQwY7htPZWs4H+mZc+Kyx7n6tpP1XByQSiUQikXisEErSed1aAdEqvF9jYBEGyE39hn4WaK+ZZX0YEixAp9V6igu/zaAt3CME3seCUyHOYUCQAPrw/oI/SuNlVAANomHXQgQX1EpGvqqHV1j7lnuAAYUCdF62Fu877Meh95CvP5EpjoAGmSg/CR5YQ2QKBfDwlYX+fzCvIjKFAlQ590chg213HoYQ49w7OhCVe2igb72nT4KJADJSfUmWPjSPsx5mnOgCMNEBNdC2SlxiE1UAefi950dZGzUimgA68uMefta9QxQBdM5r2N+89s87MG/J9gVLeLBTSbbI652NzVLeISwVLrwDGurw2jtUkUHmfUrf+QAEMLUAOvrNr9n3oYs18g5TC+CJTm9+r5t3mFoA+YPu0IWaeYfp14BGvgLf6LRe3mFqAUZX27p5hzi7QI1JAuCRY2qHy0Lkeh585vxgYfUOi475hVUKrcyEAFo3wJzY58Mv3TE/tzuvWuvSaNtCiMqnQF47eELvmodZ97Y2eaZ5SbusQkWm+jWAwxxc81vWdxIliEylAoz1EXegUSLTJWqVqVIBxEecoCRSYzxDRAoFYJ2jRjCV/2/2w6n3tBRHwIjbe2i4gAZdGCHzubRvIBd3KywUoHmU9bTeBwOc5yWURRIjRCRoEXRS7/tbeIyKrAErnTet4PO+n5IQxT4dChIgt7xyPm8RCf6K30v9rvChtNJEkg0iMsHboIogZe9PdElvpdx0Qs5FiQgi5AVUHd3b2vyQKPHz/NnibLD0O0JF/NrY6mBCeHCkdiJmaLA9ztESrvyyThVMyLPj/eZdv8+WG9QRZjE918Pi2WCIhkkFETxykgCIjKV3KEtI7hI/AmbIO2h5rahNdAEsvUNZ3IJrF7ZBZCy9Qxn0HkIqTSaLoJV3CEX7diPvK9zR1ob8HYFL7BDzJu4RHXlX4h0l4zxrYGJ8AzvO0RJ7m4NTgut74lNdf3QKIpFIJBKJRCKAPxZBM7U9oOuSAAAAAElFTkSuQmCC" alt="icon for blast radius items" /> Affected items | |
| 133 | +| ------------- | |
| 134 | +| [%v items](%v) | |
| 135 | +`, changeUrl, response.Msg.Change.Metadata.NumAffectedItems, changeUrl) |
| 136 | + } else { |
| 137 | + fmt.Printf(`## Blast Radius · [View in Overmind](%v) <img align="center" width="16" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAPwSURBVHgB7VrdVdswGFVCBkg3cDdIJyBMUHyAZ5INyASlE6SdAPPKX+gENRPABmSDZgBIem9ipZ8VKZaR7fRwuOfk2LJk+V79fLoSKPWB3aKl/gNMJpMuLvzN4jielXl3ZwJAuo/L6Xw+5zUSWRSQ4vfr6OgoKaqncQEg3lssFmP8+h7Fpyg3Oj4+vncVaKsGcXd3xxb/7UmeiFqtFjRPvrkKNCaA5EE8UauxrpFA0EG73f6E4dLilWmUu5Tv4tm5S0QjQ0iQ15iCLOZr/OR6B4Qj9pYS8wPvHOCdVJarvQcc5A+2kSeQvyzH8voZBF2Y5WoVsIX81Of9TEQsHkU3NzeHskxtAkLJa7CnjDkxkPm1CKiKvIasC1FpX+ZVLqBq8kSn05HzpZut3EtUKqAO8oTFXqwFVBZG6yJPsMURgf7oNNcMfV9JD/iQl91eFi8vLz1Zt8wLFsAFB+R/yA+Y5BH6HtmCt7e3tAWRKgnUdyqSaS5PBQLExurfmLS1PP2MbsFDlH9kj3lWrzLBA51G/TmbESSAzpKkROUbY55pkP4uHnU53HxECDuxBN57qtRKoMKBSCauCXtycnIOcZ+VGL8cdtuGk8ULzfb29mKzXJAAfGC9qJhda8LibdgTF7ayNiMHjGwNFCQAhCKRfCoqb3ob7guyndkaDvJD1+4sdAh1BTmvvazpbV5fX3PmrAx5wksAJxxC4DN+Y/kc0WVNukycN9aMfVEHg0Ik8oZF++JCAWKRivA7kxMPrTUVRXvKE9LboBEifZ/1zk+1midDn019Z1umbYWVEwnj+QH5S+IQw7CYKg9wuKE3dTLXc9jAn+Fypjzh7AGXPZBlMH7lacHg6urKqxeM4VbqHMiEVYCvMUN8TzEEUp1GnPayCtLb4DtTFYANAZm3ScSjra4SAoZiMi9DYJEIw9s8qABsCJBLt/KwxMzj4ZN4RBHPrmMQ09ug1xIVgNx+4Pr6ug/CawFc/n39PCblAD0xlmsDz3nk+mBZpO4RaWIVgFwPGF2blNmMMORBwBeSYppzo4D8DN8bqUCYYbSvb4q8jQ2Z4JhRpoA8Mapit2bOgUjcF3obFzzIey1SPmjLD7lIvBV1kydkD8yMj795D5u9Xzt5Yi2ALS7NmSrhbUw0RZ7IzQGEwFTfZ96mNJokT5gCcmeQvt5Go2nyxMbBFn2/IOB9OLUL8oTNSgxF0svbcCOyC/KE9WgRloKnCKaXSbLFjUcbsyxK9bK5MjDKNkKecJ6NOkQUgVFs1BR5YuvhLv8aQoOm8kPDXhG8D611FfagDLxOp+k0cfmqVl5JLnCc5Ck2KJfc3KgdoPTxesi/BXzgPeIvmnKmx43NP2kAAAAASUVORK5CYII=" alt="chain link icon" /> |
| 138 | +
|
| 139 | +> **✅ Checks complete** |
| 140 | +> Overmind didn't identify any potentially affected apps and items as a result of this pull request. |
| 141 | +
|
| 142 | +`, changeUrl) |
| 143 | + } |
| 144 | + } |
| 145 | + |
| 146 | + return 0 |
| 147 | +} |
| 148 | + |
| 149 | +func init() { |
| 150 | + rootCmd.AddCommand(getChangeCmd) |
| 151 | + |
| 152 | + getChangeCmd.PersistentFlags().String("change", "", "The frontend URL of the change to get") |
| 153 | + getChangeCmd.PersistentFlags().String("uuid", "", "The UUID of the change that should be displayed.") |
| 154 | + getChangeCmd.MarkFlagsMutuallyExclusive("change", "uuid") |
| 155 | + |
| 156 | + getChangeCmd.PersistentFlags().String("frontend", "https://app.overmind.tech/", "The frontend base URL") |
| 157 | + getChangeCmd.PersistentFlags().String("format", "json", "How to render the change. Possible values: json, markdown") |
| 158 | + |
| 159 | + getChangeCmd.PersistentFlags().String("timeout", "1m", "How long to wait for responses") |
| 160 | +} |
0 commit comments