fix: make installer not rely on github API

fixes #3949
port 2.0
This commit is contained in:
bbedward committed 2026-10-30 13:38:19 -04:00
1 parent 16fc39cc16
commit 7cf13f150a
2 files changed
+73 -5

No files matched your search

+13 -3
View File
@@ -35,11 +35,21 @@ aarch64)
;;
esac
# Get the latest release version
LATEST_VERSION=$(curl -sL https://api.github.com/repos/duxlabs-dev/dux-shell/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
REPO_URL="https://github.com/duxlabs-dev/dux-shell"
# The web redirect has no per-ip quota; api.github.com allows 60 unauthenticated requests per hour per ip
LATEST_VERSION=$(curl -sI "$REPO_URL/releases/latest" | tr -d '\r' | awk 'tolower($1) == "location:" { sub(".*/releases/tag/", "", $2); print $2 }')
if [ -z "$LATEST_VERSION" ]; then
API_RESPONSE=$(curl -sL https://api.github.com/repos/duxlabs-dev/dux-shell/releases/latest)
LATEST_VERSION=$(printf "%s" "$API_RESPONSE" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
fi
if [ -z "$LATEST_VERSION" ]; then
printf "%bError: Could not fetch latest version%b\n" "$RED" "$NC"
if printf "%s" "$API_RESPONSE" | grep -q "rate limit"; then
printf "GitHub API rate limit reached for your IP address (60 requests per hour). Wait an hour or install from a different network.\n"
fi
exit 1
fi
@@ -52,7 +62,7 @@ cd "$TEMP_DIR" || exit 1
# Download the gzipped binary and its checksum
printf "%bDownloading installer...%b\n" "$GREEN" "$NC"
RELEASE_URL="https://github.com/duxlabs-dev/dux-shell/releases/download/$LATEST_VERSION"
RELEASE_URL="$REPO_URL/releases/download/$LATEST_VERSION"
# install.duxlabs.dev serves this file from master; releases before 2.0.1 only ship dankinstall-*.gz
ASSET="duxinstall-$ARCH.gz"
if ! curl -fsLI "$RELEASE_URL/$ASSET" >/dev/null; then
+60 -2
View File
@@ -3,14 +3,72 @@ package netfetch
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"path"
"strings"
"time"
)
const latestReleaseURL = "https://api.github.com/repos/duxlabs-dev/dux-shell/releases/latest"
const (
latestReleaseRedirectURL = "https://github.com/duxlabs-dev/dux-shell/releases/latest"
latestReleaseAPIURL = "https://api.github.com/repos/duxlabs-dev/dux-shell/releases/latest"
releaseLookupTimeout = 10 * time.Second
)
var ErrGitHubRateLimited = errors.New("github api rate limit exceeded (60 unauthenticated requests per hour per ip)")
func LatestReleaseTag(ctx context.Context) (string, error) {
body, err := Bytes(ctx, latestReleaseURL, Options{Timeout: 10 * time.Second})
tag, redirectErr := latestTagFromRedirect(ctx, latestReleaseRedirectURL)
if redirectErr == nil {
return tag, nil
}
tag, apiErr := latestTagFromAPI(ctx, latestReleaseAPIURL)
if apiErr == nil {
return tag, nil
}
return "", fmt.Errorf("release redirect: %w; release api: %w", redirectErr, apiErr)
}
func latestTagFromRedirect(ctx context.Context, url string) (string, error) {
opts := Options{
Timeout: releaseLookupTimeout,
CheckRedirect: func(*http.Request, []*http.Request) error {
return http.ErrUseLastResponse
},
}
req, err := opts.request(ctx, url)
if err != nil {
return "", err
}
req.Method = http.MethodHead
resp, err := opts.client().Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode < 300 || resp.StatusCode >= 400 {
return "", &StatusError{Code: resp.StatusCode}
}
location := resp.Header.Get("Location")
tag := path.Base(location)
if tag == "" || tag == "." || tag == "/" || !strings.Contains(location, "/releases/tag/") {
return "", fmt.Errorf("unexpected release redirect: %q", location)
}
return tag, nil
}
func latestTagFromAPI(ctx context.Context, url string) (string, error) {
body, err := Bytes(ctx, url, Options{Timeout: releaseLookupTimeout})
var status *StatusError
if errors.As(err, &status) && (status.Code == http.StatusForbidden || status.Code == http.StatusTooManyRequests) {
return "", ErrGitHubRateLimited
}
if err != nil {
return "", err
}