|
| 1 | +package cmd |
| 2 | + |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + "encoding/json" |
| 6 | + "fmt" |
| 7 | + "os" |
| 8 | + "os/signal" |
| 9 | + "syscall" |
| 10 | + "time" |
| 11 | + |
| 12 | + "github.com/bufbuild/connect-go" |
| 13 | + "github.com/google/uuid" |
| 14 | + "github.com/overmindtech/ovm-cli/tracing" |
| 15 | + "github.com/overmindtech/sdp-go" |
| 16 | + log "github.com/sirupsen/logrus" |
| 17 | + "github.com/spf13/cobra" |
| 18 | + "github.com/spf13/viper" |
| 19 | + "go.opentelemetry.io/otel/attribute" |
| 20 | + "go.opentelemetry.io/otel/trace" |
| 21 | +) |
| 22 | + |
| 23 | +// getChangeCmd represents the get-change command |
| 24 | +var getChangeCmd = &cobra.Command{ |
| 25 | + Use: "get-change {--uuid ID | --change https://app.overmind.tech/changes/c772d072-6b0b-4763-b7c5-ff5069beed4c}", |
| 26 | + Short: "Displays the contents of a change.", |
| 27 | + PreRun: func(cmd *cobra.Command, args []string) { |
| 28 | + // Bind these to viper |
| 29 | + err := viper.BindPFlags(cmd.Flags()) |
| 30 | + if err != nil { |
| 31 | + log.WithError(err).Fatal("could not bind `get-change` flags") |
| 32 | + } |
| 33 | + }, |
| 34 | + Run: func(cmd *cobra.Command, args []string) { |
| 35 | + sigs := make(chan os.Signal, 1) |
| 36 | + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) |
| 37 | + |
| 38 | + exitcode := GetChange(sigs, nil) |
| 39 | + tracing.ShutdownTracer() |
| 40 | + os.Exit(exitcode) |
| 41 | + }, |
| 42 | +} |
| 43 | + |
| 44 | +func GetChange(signals chan os.Signal, ready chan bool) int { |
| 45 | + timeout, err := time.ParseDuration(viper.GetString("timeout")) |
| 46 | + if err != nil { |
| 47 | + log.Errorf("invalid --timeout value '%v', error: %v", viper.GetString("timeout"), err) |
| 48 | + return 1 |
| 49 | + } |
| 50 | + |
| 51 | + ctx := context.Background() |
| 52 | + ctx, span := tracing.Tracer().Start(ctx, "CLI GetChange", trace.WithAttributes( |
| 53 | + attribute.String("om.config", fmt.Sprintf("%v", viper.AllSettings())), |
| 54 | + )) |
| 55 | + defer span.End() |
| 56 | + |
| 57 | + ctx, err = ensureToken(ctx, signals) |
| 58 | + if err != nil { |
| 59 | + log.WithContext(ctx).WithFields(log.Fields{ |
| 60 | + "url": viper.GetString("url"), |
| 61 | + }).WithError(err).Error("failed to authenticate") |
| 62 | + return 1 |
| 63 | + } |
| 64 | + |
| 65 | + // apply a timeout to the main body of processing |
| 66 | + ctx, cancel := context.WithTimeout(ctx, timeout) |
| 67 | + defer cancel() |
| 68 | + |
| 69 | + lf := log.Fields{} |
| 70 | + changeUuid, err := getChangeUuid(ctx, sdp.ChangeStatus(sdp.ChangeStatus_value[viper.GetString("status")])) |
| 71 | + if err != nil { |
| 72 | + log.WithError(err).WithFields(lf).Error("failed to identify change") |
| 73 | + return 1 |
| 74 | + } |
| 75 | + |
| 76 | + lf["uuid"] = changeUuid.String() |
| 77 | + |
| 78 | + client := AuthenticatedChangesClient(ctx) |
| 79 | + response, err := client.GetChange(ctx, &connect.Request[sdp.GetChangeRequest]{ |
| 80 | + Msg: &sdp.GetChangeRequest{ |
| 81 | + UUID: changeUuid[:], |
| 82 | + }, |
| 83 | + }) |
| 84 | + if err != nil { |
| 85 | + log.WithContext(ctx).WithError(err).WithFields(log.Fields{ |
| 86 | + "change-url": viper.GetString("change-url"), |
| 87 | + }).Error("failed to get change") |
| 88 | + return 1 |
| 89 | + } |
| 90 | + log.WithContext(ctx).WithFields(log.Fields{ |
| 91 | + "change-uuid": uuid.UUID(response.Msg.Change.Metadata.UUID), |
| 92 | + "change-created": response.Msg.Change.Metadata.CreatedAt.AsTime(), |
| 93 | + "change-name": response.Msg.Change.Properties.Title, |
| 94 | + "change-description": response.Msg.Change.Properties.Description, |
| 95 | + }).Info("found change") |
| 96 | + |
| 97 | + switch viper.GetString("format") { |
| 98 | + case "json": |
| 99 | + b, _ := json.MarshalIndent(response.Msg.Change, "", " ") |
| 100 | + fmt.Println(string(b)) |
| 101 | + case "markdown": |
| 102 | + changeUrl := fmt.Sprintf("%v/changes/%v", viper.GetString("frontend"), changeUuid.String()) |
| 103 | + if response.Msg.Change.Metadata.NumAffectedApps != 0 || response.Msg.Change.Metadata.NumAffectedItems != 0 { |
| 104 | + // we have affected stuff |
| 105 | + 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" /> |
| 106 | +
|
| 107 | +> **Warning** |
| 108 | +> Overmind identified potentially affected apps and items as a result of this pull request. |
| 109 | +
|
| 110 | +<br> |
| 111 | +
|
| 112 | +| <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 | |
| 113 | +| ------------- | |
| 114 | +| [%v items](%v) | |
| 115 | +`, changeUrl, response.Msg.Change.Metadata.NumAffectedItems, changeUrl) |
| 116 | + } else { |
| 117 | + 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" /> |
| 118 | +
|
| 119 | +> **✅ Checks complete** |
| 120 | +> Overmind didn't identify any potentially affected apps and items as a result of this pull request. |
| 121 | +
|
| 122 | +`, changeUrl) |
| 123 | + } |
| 124 | + } |
| 125 | + |
| 126 | + return 0 |
| 127 | +} |
| 128 | + |
| 129 | +func init() { |
| 130 | + rootCmd.AddCommand(getChangeCmd) |
| 131 | + |
| 132 | + withChangeUuidFlags(getChangeCmd) |
| 133 | + getChangeCmd.PersistentFlags().String("status", "", "The expected status of the change. Use this with --ticket-link. Allowed values: CHANGE_STATUS_UNSPECIFIED, CHANGE_STATUS_DEFINING, CHANGE_STATUS_HAPPENING, CHANGE_STATUS_PROCESSING, CHANGE_STATUS_DONE") |
| 134 | + |
| 135 | + getChangeCmd.PersistentFlags().String("frontend", "https://app.overmind.tech/", "The frontend base URL") |
| 136 | + getChangeCmd.PersistentFlags().String("format", "json", "How to render the change. Possible values: json, markdown") |
| 137 | + |
| 138 | + getChangeCmd.PersistentFlags().String("timeout", "1m", "How long to wait for responses") |
| 139 | +} |
0 commit comments