github.com/nextlinux/gosbom@v0.81.1-0.20230627115839-1ff50c281391/internal/version/update.go (about)

     1  package version
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"net/http"
     7  	"strings"
     8  
     9  	"github.com/nextlinux/gosbom/internal"
    10  
    11  	hashiVersion "github.com/anchore/go-version"
    12  )
    13  
    14  var latestAppVersionURL = struct {
    15  	host string
    16  	path string
    17  }{
    18  	host: "https://toolbox-data.anchore.io",
    19  	path: fmt.Sprintf("/%s/releases/latest/VERSION", internal.ApplicationName),
    20  }
    21  
    22  // IsUpdateAvailable indicates if there is a newer application version available, and if so, what the new version is.
    23  func IsUpdateAvailable() (bool, string, error) {
    24  	currentBuildInfo := FromBuild()
    25  	if !currentBuildInfo.IsProductionBuild() {
    26  		// don't allow for non-production builds to check for a version.
    27  		return false, "", nil
    28  	}
    29  	currentVersion, err := hashiVersion.NewVersion(currentBuildInfo.Version)
    30  	if err != nil {
    31  		return false, "", fmt.Errorf("failed to parse current application version: %w", err)
    32  	}
    33  
    34  	latestVersion, err := fetchLatestApplicationVersion()
    35  	if err != nil {
    36  		return false, "", err
    37  	}
    38  
    39  	if latestVersion.GreaterThan(currentVersion) {
    40  		return true, latestVersion.String(), nil
    41  	}
    42  
    43  	return false, "", nil
    44  }
    45  
    46  func fetchLatestApplicationVersion() (*hashiVersion.Version, error) {
    47  	req, err := http.NewRequest(http.MethodGet, latestAppVersionURL.host+latestAppVersionURL.path, nil)
    48  	if err != nil {
    49  		return nil, fmt.Errorf("failed to create request for latest version: %w", err)
    50  	}
    51  
    52  	client := http.Client{}
    53  	resp, err := client.Do(req)
    54  	if err != nil {
    55  		return nil, fmt.Errorf("failed to fetch latest version: %w", err)
    56  	}
    57  	defer resp.Body.Close()
    58  
    59  	if resp.StatusCode != http.StatusOK {
    60  		return nil, fmt.Errorf("HTTP %d on fetching latest version: %s", resp.StatusCode, resp.Status)
    61  	}
    62  
    63  	versionBytes, err := io.ReadAll(resp.Body)
    64  	if err != nil {
    65  		return nil, fmt.Errorf("failed to read latest version: %w", err)
    66  	}
    67  
    68  	versionStr := strings.TrimSuffix(string(versionBytes), "\n")
    69  	if len(versionStr) > 50 {
    70  		return nil, fmt.Errorf("version too long: %q", versionStr[:50])
    71  	}
    72  
    73  	return hashiVersion.NewVersion(versionStr)
    74  }