-
Notifications
You must be signed in to change notification settings - Fork 298
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Jan Dubois <[email protected]>
- Loading branch information
Showing
10 changed files
with
293 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -288,6 +288,7 @@ gcs | |
GENERALIZEDTIME | ||
getfattr | ||
getwindowid | ||
gha | ||
ghp | ||
gitmodules | ||
gitrepo | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
name: Update k3s-versions.json | ||
on: | ||
schedule: | ||
- cron: '43 8 * * *' | ||
workflow_dispatch: {} | ||
|
||
permissions: | ||
contents: write | ||
pull-requests: write | ||
|
||
jobs: | ||
check-for-token: | ||
outputs: | ||
has-token: ${{ steps.calc.outputs.HAS_SECRET }} | ||
runs-on: ubuntu-latest | ||
steps: | ||
- id: calc | ||
run: echo "HAS_SECRET=${HAS_SECRET}" >> "${GITHUB_OUTPUT}" | ||
env: | ||
HAS_SECRET: ${{ secrets.RUN_WORKFLOW_FROM_WORKFLOW != '' }} | ||
|
||
check-update-versions: | ||
needs: check-for-token | ||
if: needs.check-for-token.outputs.has-token == 'true' | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||
with: | ||
# we may need to checkout an existing branch, so need the full history | ||
fetch-depth: 0 | ||
# Setup go to be able to run `go run ./scripts/k3s-version.go` | ||
- uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 | ||
with: | ||
go-version-file: go.work | ||
- run: ./scripts/k3s-versions.sh | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.RUN_WORKFLOW_FROM_WORKFLOW }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module github.com/rancher-sandbox/rancher-desktop/scripts | ||
|
||
go 1.23 | ||
|
||
require golang.org/x/mod v0.22.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= | ||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"maps" | ||
"net/http" | ||
"os" | ||
"slices" | ||
"strconv" | ||
"strings" | ||
|
||
"golang.org/x/mod/semver" | ||
) | ||
|
||
// golang.org/x/mod/semver *requires* a leading 'v' on versions, and will add missing minor/patch numbers. | ||
const minimumVersion = "v1.21" | ||
|
||
type Channels struct { | ||
Data []Channel `json:"data"` | ||
} | ||
type Channel struct { | ||
Name string `json:"name"` | ||
Latest string `json:"latest"` | ||
} | ||
|
||
// getK3sChannels returns a map of all non-prerelease channels, plus "latest" and "stable". | ||
// The values are the latest release for each channel. | ||
func getK3sChannels() (map[string]string, error) { | ||
resp, err := http.Get("https://update.k3s.io/v1-release/channels") | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to get k3s channels: %w", err) | ||
} | ||
defer resp.Body.Close() | ||
if resp.StatusCode != http.StatusOK { | ||
return nil, fmt.Errorf("update channel request failed with status: %s", resp.Status) | ||
} | ||
|
||
body, err := io.ReadAll(resp.Body) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var channels Channels | ||
if err := json.Unmarshal(body, &channels); err != nil { | ||
return nil, err | ||
} | ||
|
||
k3sChannels := make(map[string]string) | ||
for _, channel := range channels.Data { | ||
switch { | ||
case channel.Name == "latest" || channel.Name == "stable": | ||
break | ||
case semver.Prerelease(channel.Latest) != "": | ||
continue | ||
case semver.IsValid(channel.Latest) && semver.Compare(channel.Latest, minimumVersion) >= 0: | ||
break | ||
default: | ||
continue | ||
} | ||
// Turn "v1.31.3+k3s1" into "1.31.3" | ||
latest := strings.TrimPrefix(channel.Latest, "v") | ||
latest = strings.SplitN(latest, "+", 2)[0] | ||
k3sChannels[channel.Name] = latest | ||
} | ||
|
||
return k3sChannels, nil | ||
} | ||
|
||
type GithubRelease struct { | ||
TagName string `json:"tag_name"` | ||
Draft bool `json:"draft"` | ||
Prerelease bool `json:"prerelease"` | ||
} | ||
|
||
// getGithubReleasesPage fetches a single page of GitHub releases and returns a list | ||
// of all non-draft, non-prerelease releases higher than the minimumVersion. | ||
func getGithubReleasesPage(page int) ([]GithubRelease, error) { | ||
url := fmt.Sprintf("https://api.github.com/repos/k3s-io/k3s/releases?page=%d", page) | ||
req, err := http.NewRequest("GET", url, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
token := os.Getenv("GH_TOKEN") | ||
if token == "" { | ||
token = os.Getenv("GITHUB_TOKEN") | ||
} | ||
if token != "" { | ||
req.Header.Set("Authorization", "token "+token) | ||
} | ||
|
||
client := &http.Client{} | ||
resp, err := client.Do(req) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer resp.Body.Close() | ||
if resp.StatusCode != http.StatusOK { | ||
//nolint:revive // error-strings | ||
return nil, fmt.Errorf("GitHub API request failed with status: %s", resp.Status) | ||
} | ||
|
||
body, err := io.ReadAll(resp.Body) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var releases []GithubRelease | ||
if err := json.Unmarshal(body, &releases); err != nil { | ||
return nil, err | ||
} | ||
|
||
// Filter desired releases here, so caller will stop requesting additional pages if there are | ||
// no more matches (heuristics, but releases are returned in reverse chronological order). | ||
releases = slices.DeleteFunc(releases, func(release GithubRelease) bool { | ||
return release.Draft || release.Prerelease || semver.Compare(release.TagName, minimumVersion) < 0 | ||
}) | ||
return releases, nil | ||
} | ||
|
||
// getGithubReleases returns a sorted list of all matching GitHub releases. | ||
func getGithubReleases() ([]string, error) { | ||
releaseMap := make(map[string]string) | ||
for page := 1; ; page++ { | ||
releases, err := getGithubReleasesPage(page) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if len(releases) == 0 { | ||
break | ||
} | ||
for _, release := range releases { | ||
version := semver.Canonical(release.TagName) | ||
// for each version we only keep the latest k3s patch, i.e. +k3s2 instead of +k3s1 | ||
if oldTag, ok := releaseMap[version]; ok { | ||
oldPatch, _ := strconv.Atoi(strings.TrimPrefix(semver.Build(oldTag), "+k3s")) | ||
patch, _ := strconv.Atoi(strings.TrimPrefix(semver.Build(release.TagName), "+k3s")) | ||
if oldPatch > patch { | ||
continue | ||
} | ||
} | ||
releaseMap[version] = release.TagName | ||
} | ||
} | ||
|
||
return slices.SortedFunc(maps.Values(releaseMap), semver.Compare), nil | ||
} | ||
|
||
func getK3sVersions() (string, error) { | ||
k3sChannels, err := getK3sChannels() | ||
if err != nil { | ||
return "", fmt.Errorf("error fetching k3s channels: %w", err) | ||
} | ||
|
||
githubReleases, err := getGithubReleases() | ||
if err != nil { | ||
return "", fmt.Errorf("error fetching GitHub releases: %w", err) | ||
} | ||
|
||
result := map[string]interface{}{ | ||
"cacheVersion": 2, | ||
"channels": k3sChannels, | ||
"versions": githubReleases, | ||
} | ||
|
||
// json.Marshal will produce map keys in sort order | ||
jsonResult, err := json.MarshalIndent(result, "", " ") | ||
if err != nil { | ||
return "", fmt.Errorf("error marshalling result to JSON: %w", err) | ||
} | ||
return string(jsonResult), nil | ||
} | ||
|
||
func main() { | ||
versions, err := getK3sVersions() | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
fmt.Println(versions) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
#!/bin/bash | ||
|
||
# This script expects to be called from the root of the repo. | ||
# It will rebuild resources/k3s-versions.json from both the k3s update | ||
# channel and the GitHub k3s releases list. | ||
# Creates a pull request if the new version is different. | ||
|
||
set -eu | ||
|
||
K3S_VERSIONS="resources/k3s-versions.json" | ||
BRANCH_NAME="gha-update-k3s-versions" | ||
NEW_PR="true" | ||
|
||
if git rev-parse --verify "origin/${BRANCH_NAME}" 2>/dev/null; then | ||
# This logic relies on the fact that PR branches inside the repo get automatically | ||
# deleted when the PR has been merged. We assume that if the branch exists, there | ||
# is also a corresponding PR for it, so we just update the branch with a new commit. | ||
git checkout "$BRANCH_NAME" | ||
NEW_PR="false" | ||
else | ||
git checkout -b "$BRANCH_NAME" | ||
fi | ||
|
||
go run ./scripts/k3s-versions.go "$MINIMUM_VERSION" >"$K3S_VERSIONS" | ||
|
||
# Exit if there are no changes | ||
if git diff --exit-code; then | ||
exit | ||
fi | ||
|
||
export GIT_CONFIG_COUNT=2 | ||
export GIT_CONFIG_KEY_0=user.name | ||
export GIT_CONFIG_VALUE_0="Rancher Desktop GitHub Action" | ||
export GIT_CONFIG_KEY_1=user.email | ||
export GIT_CONFIG_VALUE_1="[email protected]" | ||
|
||
git add "$K3S_VERSIONS" | ||
git commit --signoff --message "Automated update: k3s-versions.json" | ||
git push origin "$BRANCH_NAME" | ||
|
||
if [ "$NEW_PR" = "false" ]; then | ||
exit | ||
fi | ||
|
||
gh pr create \ | ||
--title "Update k3s-versions.json" \ | ||
--body "This pull request contains the latest update to k3s-versions.json." \ | ||
--head "$BRANCH_NAME" \ | ||
--base main |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters