func (gcs *GCSRepo) DownloadCandidate(version, destDir, destFile string) (string, error) {
	if !strings.Contains(version, "rc") {
		return "", fmt.Errorf("'%s' does not refer to a release candidate", version)
	}
	srcFile, err := platforms.DetermineBazelFilename(version, true)
	if err != nil {
		return "", err
	}
	versionComponents := strings.Split(version, "rc")
	baseVersion := versionComponents[0]
	rcVersion := "rc" + versionComponents[1]
	url := fmt.Sprintf("%s/%s/%s/%s", candidateBaseURL, baseVersion, rcVersion, srcFile)
	return httputil.DownloadBinary(url, destDir, destFile)
}
DownloadCandidate downloads the given release candidate into the specified location and returns the absolute path.
package repositories
import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"strings"
	"github.com/bazelbuild/bazelisk/httputil"
	"github.com/bazelbuild/bazelisk/platforms"
	"github.com/bazelbuild/bazelisk/versions"
)
const (
	candidateBaseURL    = "https://releases.bazel.build"
	nonCandidateBaseURL = "https://storage.googleapis.com/bazel-builds/artifacts"
	lastGreenBaseURL    = "https://storage.googleapis.com/bazel-untrusted-builds/last_green_commit/"
)
var (
	lastGreenCommitPathSuffixes = map[bool]string{
		false: "github.com/bazelbuild/bazel.git/bazel-bazel",
		true:  "downstream_pipeline",
	}
)
type GCSRepo struct{}
func (gcs *GCSRepo) GetReleaseVersions(bazeliskHome string, lastN int) ([]string, error) {
	history, err := getVersionHistoryFromGCS()
	if err != nil {
		return []string{}, err
	}
	releases, err := gcs.removeCandidates(history, lastN)
	if err != nil {
		return []string{}, err
	}
	if len(releases) == 0 {
		return []string{}, errors.New("there are no releases available")
	}
	return releases, nil
}
func getVersionHistoryFromGCS() ([]string, error) {
	prefixes, _, err := listDirectoriesInReleaseBucket("")
	if err != nil {
		return []string{}, fmt.Errorf("could not list Bazel versions in GCS bucket: %v", err)
	}
	available := getVersionsFromGCSPrefixes(prefixes)
	sorted := versions.GetInAscendingOrder(available)
	return sorted, nil
}
func listDirectoriesInReleaseBucket(prefix string) ([]string, bool, error) {
	baseURL := "https://www.googleapis.com/storage/v1/b/bazel/o?delimiter=/"
	if prefix != "" {
		baseURL = fmt.Sprintf("%s&prefix=%s", baseURL, prefix)
	}
	var prefixes []string
	var isRelease = false
	var nextPageToken = ""
	for {
		var url = baseURL
		if nextPageToken != "" {
			url = fmt.Sprintf("%s&pageToken=%s", baseURL, nextPageToken)
		}
		content, _, err := httputil.ReadRemoteFile(url, "")
		if err != nil {
			return nil, false, fmt.Errorf("could not list GCS objects at %s: %v", url, err)
		}
		var response GcsListResponse
		if err := json.Unmarshal(content, &response); err != nil {
			return nil, false, fmt.Errorf("could not parse GCS index JSON: %v", err)
		}
		prefixes = append(prefixes, response.Prefixes...)
		isRelease = isRelease || len(response.Items) > 0
		if response.NextPageToken == "" {
			break
		}
		nextPageToken = response.NextPageToken
	}
	return prefixes, isRelease, nil
}
func getVersionsFromGCSPrefixes(versions []string) []string {
	result := make([]string, len(versions))
	for i, v := range versions {
		noSlashes := strings.Replace(v, "/", "", -1)
		result[i] = strings.TrimSuffix(noSlashes, "release")
	}
	return result
}
type GcsListResponse struct {
	Prefixes []string `json:"prefixes"`
	Items []interface{} `json:"items"`
	NextPageToken string `json:"nextPageToken"`
}
func (gcs *GCSRepo) DownloadRelease(version, destDir, destFile string) (string, error) {
	srcFile, err := platforms.DetermineBazelFilename(version, true)
	if err != nil {
		return "", err
	}
	url := fmt.Sprintf("%s/%s/release/%s", candidateBaseURL, version, srcFile)
	return httputil.DownloadBinary(url, destDir, destFile)
}
func (gcs *GCSRepo) removeCandidates(history []string, lastN int) ([]string, error) {
	var resolvedLimit int
	if lastN < 1 {
		resolvedLimit = len(history)
	} else {
		resolvedLimit = lastN
	}
	descendingReleases := make([]string, 0)
	for hpos := len(history) - 1; hpos >= 0 && len(descendingReleases) < resolvedLimit; hpos-- {
		latestVersion := history[hpos]
		_, isRelease, err := listDirectoriesInReleaseBucket(latestVersion + "/release/")
		if err != nil {
			return []string{}, fmt.Errorf("could not list available releases for %v: %v", latestVersion, err)
		}
		if isRelease {
			descendingReleases = append(descendingReleases, latestVersion)
		}
	}
	if lastN > 0 && len(descendingReleases) < lastN {
		return []string{}, fmt.Errorf("requested %d latest releases, but only found %d", lastN, len(descendingReleases))
	}
	return reverseInPlace(descendingReleases), nil
}
func reverseInPlace(values []string) []string {
	for i := 0; i < len(values)/2; i++ {
		j := len(values) - 1 - i
		values[i], values[j] = values[j], values[i]
	}
	return values
}
func (gcs *GCSRepo) GetCandidateVersions(bazeliskHome string) ([]string, error) {
	history, err := getVersionHistoryFromGCS()
	if err != nil {
		return []string{}, err
	}
	if len(history) == 0 {
		return []string{}, errors.New("could not find any Bazel versions")
	}
	for pos := len(history) - 1; pos >= 0; pos-- {
		bucket := fmt.Sprintf("%s/", history[pos])
		rcPrefixes, _, err := listDirectoriesInReleaseBucket(bucket)
		if err != nil {
			return []string{}, fmt.Errorf("could not list release candidates for latest release: %v", err)
		}
		rcs := make([]string, 0)
		for _, v := range getVersionsFromGCSPrefixes(rcPrefixes) {
			if strings.Contains(v, "rc") {
				rcs = append(rcs, v)
			}
		}
		if len(rcs) > 0 {
			return rcs, nil
		}
	}
	return nil, nil
}