github.com/turbot/steampipe@v1.7.0-rc.0.0.20240517123944-7cef272d4458/pkg/task/available_versions.go (about) 1 package task 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "os" 8 "sort" 9 10 "github.com/Masterminds/semver/v3" 11 "github.com/fatih/color" 12 "github.com/olekukonko/tablewriter" 13 "github.com/turbot/steampipe/pkg/constants" 14 "github.com/turbot/steampipe/pkg/plugin" 15 "github.com/turbot/steampipe/pkg/utils" 16 ) 17 18 type AvailableVersionCache struct { 19 StructVersion uint32 `json:"struct_version"` 20 CliCache *CLIVersionCheckResponse `json:"cli_version"` 21 PluginCache map[string]plugin.VersionCheckReport `json:"plugin_version"` 22 } 23 24 func (av *AvailableVersionCache) asTable(ctx context.Context) (*bytes.Buffer, error) { 25 notificationLines, err := av.buildNotification(ctx) 26 if err != nil { 27 return nil, err 28 } 29 notificationTable := utils.Map(notificationLines, func(line string) []string { 30 return []string{line} 31 }) 32 33 if len(notificationLines) == 0 { 34 return nil, nil 35 } 36 37 // create a buffer writer to pass to the tablewriter 38 // so that we can capture the output 39 var buffer bytes.Buffer // c 40 41 table := tablewriter.NewWriter(&buffer) 42 table.SetHeader([]string{}) // no headers please 43 table.SetAlignment(tablewriter.ALIGN_LEFT) // we align to the left 44 table.SetAutoWrapText(false) // let's not wrap the text 45 table.SetBorder(true) // there needs to be a border to provide the dialog feel 46 table.AppendBulk(notificationTable) // Add Bulk Data 47 48 // render the table into the buffer 49 table.Render() 50 return &buffer, nil 51 } 52 53 func (av *AvailableVersionCache) buildNotification(ctx context.Context) ([]string, error) { 54 cliLines, err := av.cliNotificationMessage() 55 if err != nil { 56 return nil, err 57 } 58 pluginLines := av.pluginNotificationMessage(ctx) 59 // convert notificationLines into an array of arrays 60 // since that's what our table renderer expects 61 return append(cliLines, pluginLines...), nil 62 } 63 64 func (av *AvailableVersionCache) cliNotificationMessage() ([]string, error) { 65 info := av.CliCache 66 if info == nil { 67 return nil, nil 68 } 69 70 if info.NewVersion == "" { 71 return nil, nil 72 } 73 74 newVersion, err := semver.NewVersion(info.NewVersion) 75 if err != nil { 76 return nil, err 77 } 78 79 currentVersion, err := semver.NewVersion(currentVersion) 80 if err != nil { 81 fmt.Println(fmt.Errorf("there's something wrong with the Current Version")) 82 fmt.Println(err) 83 } 84 85 if newVersion.GreaterThan(currentVersion) { 86 var downloadURLColor = color.New(color.FgYellow) 87 var notificationLines = []string{ 88 "", 89 fmt.Sprintf("A new version of Steampipe is available! %s → %s", constants.Bold(currentVersion), constants.Bold(newVersion)), 90 fmt.Sprintf("You can update by downloading from %s", downloadURLColor.Sprint("https://steampipe.io/downloads")), 91 "", 92 } 93 return notificationLines, nil 94 } 95 return nil, nil 96 } 97 98 func ppNotificationLines() []string { 99 var notificationLines = []string{ 100 "", 101 fmt.Sprintf("%s Steampipe mods and dashboards are now separately available in Powerpipe (%s), a new open-source project (%s).", constants.Bold("Introducing Powerpipe:"), color.YellowString("https://powerpipe.io"), color.YellowString("https://github.com/turbot/powerpipe")), 102 "", 103 fmt.Sprintf("The steampipe mod, check and dashboard commands %s in a future version. Migration guide - %s", constants.Bold("will be removed"), color.YellowString("https://powerpipe.io/blog/migrating-from-steampipe")), 104 "", 105 } 106 return notificationLines 107 } 108 109 func (av *AvailableVersionCache) pluginNotificationMessage(ctx context.Context) []string { 110 var pluginsToUpdate []plugin.VersionCheckReport 111 112 for _, r := range av.PluginCache { 113 if plugin.UpdateRequired(r) { 114 pluginsToUpdate = append(pluginsToUpdate, r) 115 } 116 } 117 notificationLines := []string{} 118 if len(pluginsToUpdate) > 0 { 119 notificationLines = av.getPluginNotificationLines(pluginsToUpdate) 120 } 121 return notificationLines 122 } 123 124 func (av *AvailableVersionCache) getPluginNotificationLines(reports []plugin.VersionCheckReport) []string { 125 var notificationLines = []string{ 126 "", 127 "Updated versions of the following plugins are available:", 128 "", 129 } 130 longestNameLength := 0 131 for _, report := range reports { 132 thisName := report.ShortName() 133 if len(thisName) > longestNameLength { 134 longestNameLength = len(thisName) 135 } 136 } 137 138 // sort alphabetically 139 sort.Slice(reports, func(i, j int) bool { 140 return reports[i].ShortName() < reports[j].ShortName() 141 }) 142 143 for _, report := range reports { 144 thisName := report.ShortName() 145 line := "" 146 if len(report.Plugin.Version) == 0 { 147 format := fmt.Sprintf(" %%-%ds @ %%-10s → %%10s", longestNameLength) 148 line = fmt.Sprintf( 149 format, 150 thisName, 151 report.CheckResponse.Constraint, 152 constants.Bold(report.CheckResponse.Version), 153 ) 154 } else { 155 version := report.CheckResponse.Version 156 format := fmt.Sprintf(" %%-%ds @ %%-10s %%10s → %%-10s", longestNameLength) 157 // an arm64 binary of the plugin might exist for the same version 158 if report.Plugin.Version == report.CheckResponse.Version { 159 version = fmt.Sprintf("%s (arm64)", version) 160 } 161 line = fmt.Sprintf( 162 format, 163 thisName, 164 report.CheckResponse.Constraint, 165 constants.Bold(report.Plugin.Version), 166 constants.Bold(version), 167 ) 168 } 169 notificationLines = append(notificationLines, line) 170 } 171 notificationLines = append(notificationLines, "") 172 notificationLines = append(notificationLines, fmt.Sprintf("You can update by running %s", constants.Bold("steampipe plugin update --all"))) 173 notificationLines = append(notificationLines, "") 174 175 return notificationLines 176 } 177 178 func ppNoptificationAsTable(colWidth int) (*tablewriter.Table, error) { 179 notificationLines := ppNotificationLines() 180 181 notificationTable := utils.Map(notificationLines, func(line string) []string { 182 return []string{line} 183 }) 184 185 if len(notificationLines) == 0 { 186 return nil, nil 187 } 188 189 table := tablewriter.NewWriter(os.Stdout) 190 table.SetHeader([]string{}) // no headers please 191 table.SetAlignment(tablewriter.ALIGN_LEFT) // we align to the left 192 table.SetAutoWrapText(true) // let's wrap the text 193 table.SetBorder(true) // there needs to be a border to provide the dialog feel 194 table.SetColWidth(colWidth - 4) // set the column width which matches the steampipe notification table width 195 table.AppendBulk(notificationTable) // Add Bulk Data 196 197 return table, nil 198 }