mirror of
https://github.com/juanfont/headscale.git
synced 2026-09-26 10:14:52 +09:00
24e43d0a5a
The oapi-codegen pin stays the single source of truth: the generate area reads it back out of the Makefile, so the clients are regenerated with whatever this lands on.
92 lines
2.1 KiB
Go
92 lines
2.1 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
const githubAPI = "https://api.github.com"
|
|
|
|
var errNoRelease = errors.New("no published release")
|
|
|
|
// githubToken is resolved once. Unauthenticated GitHub allows sixty requests an
|
|
// hour, which the action-pin sweep alone would exhaust, so a token is worth
|
|
// looking for even outside CI.
|
|
var githubToken = sync.OnceValue(func() string {
|
|
for _, env := range []string{"GH_TOKEN", "GITHUB_TOKEN"} {
|
|
if v := os.Getenv(env); v != "" {
|
|
return v
|
|
}
|
|
}
|
|
|
|
out, err := exec.Command("gh", "auth", "token").Output()
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
|
|
return strings.TrimSpace(string(out))
|
|
})
|
|
|
|
// githubJSON performs an authenticated GET against the GitHub REST API.
|
|
func githubJSON(ctx context.Context, path string, out any) error {
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, githubAPI+path, nil)
|
|
if err != nil {
|
|
return fmt.Errorf("building request for %s: %w", path, err)
|
|
}
|
|
|
|
req.Header.Set("Accept", "application/vnd.github+json")
|
|
|
|
if token := githubToken(); token != "" {
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
}
|
|
|
|
client := &http.Client{Timeout: proxyTimeout}
|
|
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return fmt.Errorf("GET %s: %w", path, err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("GET %s: %w: %s", path, errHTTPStatus, resp.Status)
|
|
}
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return fmt.Errorf("reading %s: %w", path, err)
|
|
}
|
|
|
|
if err := json.Unmarshal(body, out); err != nil { //nolint:noinlineerr
|
|
return fmt.Errorf("decoding %s: %w", path, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// latestRelease is the tag of a repository's newest published release.
|
|
func latestRelease(ctx context.Context, owner, name string) (string, error) {
|
|
var release struct {
|
|
TagName string `json:"tag_name"`
|
|
}
|
|
|
|
err := githubJSON(ctx, fmt.Sprintf("/repos/%s/%s/releases/latest", owner, name), &release)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if release.TagName == "" {
|
|
return "", fmt.Errorf("%w for %s/%s", errNoRelease, owner, name)
|
|
}
|
|
|
|
return release.TagName, nil
|
|
}
|