mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-07 08:06:37 +08:00
sync ci
This commit is contained in:
@@ -70,6 +70,7 @@ type githubRelease struct {
|
||||
type githubAsset struct {
|
||||
Name string `json:"name"`
|
||||
BrowserDownloadURL string `json:"browser_download_url"`
|
||||
Digest string `json:"digest"`
|
||||
}
|
||||
|
||||
// CheckAndUpdate checks for a newer release on GitHub and performs an update if available.
|
||||
@@ -99,29 +100,13 @@ func (s *Service) CheckAndUpdate(ctx context.Context, repo string, options Updat
|
||||
|
||||
slog.Info(s.logLabel+" update available", "from", localVersion, "to", remoteVersion)
|
||||
assetName := s.assetNameForGOOSGOARCH(runtime.GOOS, runtime.GOARCH)
|
||||
checksumAssetName := assetName + ".sha256"
|
||||
|
||||
var downloadURL string
|
||||
var checksumURL string
|
||||
for _, asset := range release.Assets {
|
||||
switch asset.Name {
|
||||
case assetName:
|
||||
downloadURL = asset.BrowserDownloadURL
|
||||
case checksumAssetName:
|
||||
checksumURL = asset.BrowserDownloadURL
|
||||
}
|
||||
}
|
||||
if downloadURL == "" {
|
||||
s.lastCheckKey = checkKey
|
||||
return fmt.Errorf("no matching asset %q in release %s", assetName, release.TagName)
|
||||
}
|
||||
if checksumURL == "" {
|
||||
return fmt.Errorf("no matching checksum asset %q in release %s", checksumAssetName, release.TagName)
|
||||
}
|
||||
|
||||
expectedChecksum, err := s.downloadChecksum(ctx, checksumURL, assetName)
|
||||
downloadURL, expectedChecksum, err := s.resolveReleaseAsset(ctx, release, assetName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("download checksum: %w", err)
|
||||
if downloadURL == "" {
|
||||
s.lastCheckKey = checkKey
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
execPath, err := os.Executable()
|
||||
@@ -223,6 +208,52 @@ func decodeRelease(reader io.Reader) (*githubRelease, error) {
|
||||
return &release, nil
|
||||
}
|
||||
|
||||
func (s *Service) resolveReleaseAsset(ctx context.Context, release *githubRelease, assetName string) (downloadURL string, expectedChecksum string, err error) {
|
||||
checksumAssetName := assetName + ".sha256"
|
||||
var checksumURL string
|
||||
|
||||
for _, asset := range release.Assets {
|
||||
switch asset.Name {
|
||||
case assetName:
|
||||
downloadURL = asset.BrowserDownloadURL
|
||||
expectedChecksum = normalizeGitHubDigest(asset.Digest)
|
||||
case checksumAssetName:
|
||||
checksumURL = asset.BrowserDownloadURL
|
||||
}
|
||||
}
|
||||
if downloadURL == "" {
|
||||
return "", "", fmt.Errorf("no matching asset %q in release %s", assetName, release.TagName)
|
||||
}
|
||||
if expectedChecksum != "" {
|
||||
return downloadURL, expectedChecksum, nil
|
||||
}
|
||||
if checksumURL == "" {
|
||||
return downloadURL, "", fmt.Errorf("no sha256 digest or checksum asset %q in release %s", checksumAssetName, release.TagName)
|
||||
}
|
||||
|
||||
expectedChecksum, err = s.downloadChecksum(ctx, checksumURL, assetName)
|
||||
if err != nil {
|
||||
return downloadURL, "", fmt.Errorf("download checksum: %w", err)
|
||||
}
|
||||
return downloadURL, expectedChecksum, nil
|
||||
}
|
||||
|
||||
func normalizeGitHubDigest(digest string) string {
|
||||
digest = strings.TrimSpace(digest)
|
||||
if digest == "" {
|
||||
return ""
|
||||
}
|
||||
const prefix = "sha256:"
|
||||
if strings.HasPrefix(strings.ToLower(digest), prefix) {
|
||||
digest = digest[len(prefix):]
|
||||
}
|
||||
digest = strings.ToLower(digest)
|
||||
if isSHA256Hex(digest) {
|
||||
return digest
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *Service) downloadChecksum(ctx context.Context, url string, assetName string) (string, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
|
||||
@@ -77,7 +77,7 @@ func TestGetReleaseByTag(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckAndUpdateRequiresChecksumAsset(t *testing.T) {
|
||||
func TestCheckAndUpdateRequiresChecksumSource(t *testing.T) {
|
||||
assetName := testService(nil).assetNameForGOOSGOARCH(runtime.GOOS, runtime.GOARCH)
|
||||
service := testService(&http.Client{
|
||||
Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
|
||||
@@ -98,8 +98,100 @@ func TestCheckAndUpdateRequiresChecksumAsset(t *testing.T) {
|
||||
})
|
||||
|
||||
err := service.CheckAndUpdate(context.Background(), "Rain-kl/OpenFlare", UpdateOptions{})
|
||||
if err == nil || !strings.Contains(err.Error(), "no matching checksum asset") {
|
||||
t.Fatalf("expected missing checksum asset error, got %v", err)
|
||||
if err == nil || !strings.Contains(err.Error(), "no sha256 digest or checksum asset") {
|
||||
t.Fatalf("expected missing checksum source error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeGitHubDigest(t *testing.T) {
|
||||
checksum := strings.Repeat("a", sha256.Size*2)
|
||||
testCases := []struct {
|
||||
name string
|
||||
input string
|
||||
want string
|
||||
}{
|
||||
{name: "prefixed digest", input: "sha256:" + checksum, want: checksum},
|
||||
{name: "bare hex", input: checksum, want: checksum},
|
||||
{name: "empty", input: "", want: ""},
|
||||
{name: "invalid", input: "sha256:not-a-digest", want: ""},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
if got := normalizeGitHubDigest(testCase.input); got != testCase.want {
|
||||
t.Fatalf("unexpected digest: got %q want %q", got, testCase.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveReleaseAssetPrefersDigest(t *testing.T) {
|
||||
assetName := testService(nil).assetNameForGOOSGOARCH(runtime.GOOS, runtime.GOARCH)
|
||||
checksum := strings.Repeat("b", sha256.Size*2)
|
||||
service := testService(nil)
|
||||
|
||||
downloadURL, expectedChecksum, err := service.resolveReleaseAsset(context.Background(), &githubRelease{
|
||||
TagName: "v1.0.1",
|
||||
Assets: []githubAsset{
|
||||
{
|
||||
Name: assetName,
|
||||
BrowserDownloadURL: "https://example.test/agent",
|
||||
Digest: "sha256:" + checksum,
|
||||
},
|
||||
{
|
||||
Name: assetName + ".sha256",
|
||||
BrowserDownloadURL: "https://example.test/agent.sha256",
|
||||
},
|
||||
},
|
||||
}, assetName)
|
||||
if err != nil {
|
||||
t.Fatalf("expected digest resolution to succeed: %v", err)
|
||||
}
|
||||
if downloadURL != "https://example.test/agent" {
|
||||
t.Fatalf("unexpected download url: %s", downloadURL)
|
||||
}
|
||||
if expectedChecksum != checksum {
|
||||
t.Fatalf("unexpected checksum: got %s want %s", expectedChecksum, checksum)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveReleaseAssetFallsBackToChecksumAsset(t *testing.T) {
|
||||
assetName := testService(nil).assetNameForGOOSGOARCH(runtime.GOOS, runtime.GOARCH)
|
||||
checksum := strings.Repeat("c", sha256.Size*2)
|
||||
service := testService(&http.Client{
|
||||
Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
|
||||
if req.URL.String() != "https://example.test/agent.sha256" {
|
||||
t.Fatalf("unexpected request url: %s", req.URL.String())
|
||||
}
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Header: make(http.Header),
|
||||
Body: io.NopCloser(strings.NewReader(checksum + "\n")),
|
||||
}, nil
|
||||
}),
|
||||
})
|
||||
|
||||
downloadURL, expectedChecksum, err := service.resolveReleaseAsset(context.Background(), &githubRelease{
|
||||
TagName: "v1.0.1",
|
||||
Assets: []githubAsset{
|
||||
{
|
||||
Name: assetName,
|
||||
BrowserDownloadURL: "https://example.test/agent",
|
||||
},
|
||||
{
|
||||
Name: assetName + ".sha256",
|
||||
BrowserDownloadURL: "https://example.test/agent.sha256",
|
||||
},
|
||||
},
|
||||
}, assetName)
|
||||
if err != nil {
|
||||
t.Fatalf("expected checksum fallback to succeed: %v", err)
|
||||
}
|
||||
if downloadURL != "https://example.test/agent" {
|
||||
t.Fatalf("unexpected download url: %s", downloadURL)
|
||||
}
|
||||
if expectedChecksum != checksum {
|
||||
t.Fatalf("unexpected checksum: got %s want %s", expectedChecksum, checksum)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,4 +321,4 @@ func TestIsNewerSupportsPrerelease(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user