code.vegaprotocol.io/vega@v0.79.0/libs/version/github.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package version
    17  
    18  import (
    19  	"context"
    20  	"encoding/json"
    21  	"fmt"
    22  	"io"
    23  	"net/http"
    24  
    25  	"github.com/blang/semver/v4"
    26  )
    27  
    28  type githubReleaseResponse struct {
    29  	Name         string `json:"name"`
    30  	IsDraft      bool   `json:"draft"`
    31  	IsPreRelease bool   `json:"prerelease"`
    32  }
    33  
    34  func GetGithubReleaseURL(releasesURL string, v *semver.Version) string {
    35  	return fmt.Sprintf("%v/tag/v%v", releasesURL, v)
    36  }
    37  
    38  func BuildGithubReleasesRequestFrom(ctx context.Context, releasesURL string) ReleasesGetter {
    39  	return func() ([]*Version, error) {
    40  		req, err := http.NewRequestWithContext(ctx, http.MethodGet, releasesURL, nil)
    41  		if err != nil {
    42  			return nil, fmt.Errorf("couldn't build request: %w", err)
    43  		}
    44  		req.Header.Add("Accept", "application/vnd.github.v3+json")
    45  
    46  		client := &http.Client{}
    47  		resp, err := client.Do(req)
    48  		if err != nil {
    49  			return nil, fmt.Errorf("couldn't deliver request: %w", err)
    50  		}
    51  		defer resp.Body.Close()
    52  
    53  		body, err := io.ReadAll(resp.Body)
    54  		if err != nil {
    55  			return nil, fmt.Errorf("couldn't read response body: %w", err)
    56  		}
    57  
    58  		responses := []githubReleaseResponse{}
    59  		if err = json.Unmarshal(body, &responses); err != nil {
    60  			// try to parse as a general error message which would be useful information
    61  			// to know e.g. if we were blocked due to GitHub rate-limiting
    62  			m := struct {
    63  				Message string `json:"message"`
    64  			}{}
    65  			if mErr := json.Unmarshal(body, &m); mErr == nil {
    66  				return nil, fmt.Errorf("couldn't read response message: %s: %w", m.Message, err)
    67  			}
    68  
    69  			return nil, fmt.Errorf("couldn't unmarshal response body: %w", err)
    70  		}
    71  
    72  		releases := []*Version{}
    73  		for _, response := range responses {
    74  			release, err := NewVersionFromString(response.Name)
    75  			if err != nil {
    76  				// unsupported version
    77  				continue
    78  			}
    79  
    80  			// At this point, the Version has been initialised based on the
    81  			// segment of the version string. We have to readjust it based on
    82  			// GitHub metadata.
    83  
    84  			// We set the draft flag from GitHub response unconditionally as this
    85  			// can only be inferred from GitHub metadata.
    86  			release.IsDraft = response.IsDraft
    87  
    88  			// If this is not marked as pre-release already, this means it's
    89  			// either a stable version, either a temporary pre-release.
    90  			// Temporary pre-release are releases that are supposed to be genuine
    91  			// ones, but have been temporarily marked as pre-release in GitHub
    92  			// to warn incompatibility with mainnet.
    93  			// As a result, if not set, we verify if the state in GitHub.
    94  			if !release.IsPreReleased {
    95  				release.IsPreReleased = response.IsPreRelease
    96  			}
    97  
    98  			// We recompute the release flag based on the update above.
    99  			release.IsReleased = !release.IsDevelopment && !release.IsPreReleased && !release.IsDraft
   100  
   101  			releases = append(releases, release)
   102  		}
   103  
   104  		return releases, nil
   105  	}
   106  }