diff --git a/.github/workflows/update-node-version.yml b/.github/workflows/update-node-version.yml new file mode 100644 index 000000000000..eff4eff518bc --- /dev/null +++ b/.github/workflows/update-node-version.yml @@ -0,0 +1,48 @@ +name: "update-node-version" +on: + workflow_dispatch: + schedule: + # every Monday at around 3 am pacific/10 am UTC + - cron: "0 10 * * 1" +env: + GOPROXY: https://proxy.golang.org + GO_VERSION: '1.21.5' +permissions: + contents: read + +jobs: + bump-node-version: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 + - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 + with: + go-version: ${{env.GO_VERSION}} + cache-dependency-path: ./go.sum + - name: Bump node version + id: bumpNode + run: | + echo "OLD_VERSION=$(DEP=node make get-dependency-version)" >> "$GITHUB_OUTPUT" + make update-node-version + echo "NEW_VERSION=$(DEP=node make get-dependency-version)" >> "$GITHUB_OUTPUT" + # The following is to support multiline with GITHUB_OUTPUT, see https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings + echo "changes<> "$GITHUB_OUTPUT" + echo "$(git status --porcelain)" >> "$GITHUB_OUTPUT" + echo "EOF" >> "$GITHUB_OUTPUT" + - name: Create PR + if: ${{ steps.bumpNode.outputs.changes != '' }} + uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 + with: + token: ${{ secrets.MINIKUBE_BOT_PAT }} + commit-message: 'site: Update node from ${{ steps.bumpNode.outputs.OLD_VERSION }} to ${{ steps.bumpNode.outputs.NEW_VERSION }}' + committer: minikube-bot + author: minikube-bot + branch: auto_bump_node_version + push-to-fork: minikube-bot/minikube + base: master + delete-branch: true + title: 'site: Update node from ${{ steps.bumpNode.outputs.OLD_VERSION }} to ${{ steps.bumpNode.outputs.NEW_VERSION }}' + body: | + The [node](https://github.com/nodejs/node) project released a new version + + This PR was auto-generated by `make update-node-version` using [update-node-version.yml](https://github.com/kubernetes/minikube/tree/master/.github/workflows/update-node-version.yml) CI Workflow. diff --git a/Makefile b/Makefile index c3f250e1c167..61180077f12d 100644 --- a/Makefile +++ b/Makefile @@ -1200,6 +1200,11 @@ update-nerdctld-version: (cd hack/update/nerdctld_version && \ go run update_nerdctld_version.go) +.PHONY: update-node-version +update-node-version: + (cd hack/update/node_version && \ + go run update_node_version.go) + .PHONY: get-dependency-verison get-dependency-version: @(cd hack/update/get_version && \ diff --git a/hack/update/get_version/get_version.go b/hack/update/get_version/get_version.go index 053b9b53306a..0a6a3886d508 100644 --- a/hack/update/get_version/get_version.go +++ b/hack/update/get_version/get_version.go @@ -57,6 +57,7 @@ var dependencies = map[string]dependency{ "metrics-server": {addonsFile, `metrics-server/metrics-server:(.*)@`}, "nerdctl": {"deploy/kicbase/Dockerfile", `NERDCTL_VERSION="(.*)"`}, "nerdctld": {"deploy/kicbase/Dockerfile", `NERDCTLD_VERSION="(.*)"`}, + "node": {"netlify.toml", `NODE_VERSION = "(.*)"`}, "nvidia-device-plugin": {addonsFile, `nvidia/k8s-device-plugin:(.*)@`}, "registry": {addonsFile, `registry:(.*)@`}, "runc": {"deploy/iso/minikube-iso/package/runc-master/runc-master.mk", `RUNC_MASTER_VERSION = (.*)`}, diff --git a/hack/update/go_github_version/update_go_github_version.go b/hack/update/go_github_version/update_go_github_version.go index cd1ae73dad5c..0fa38f06522c 100644 --- a/hack/update/go_github_version/update_go_github_version.go +++ b/hack/update/go_github_version/update_go_github_version.go @@ -61,13 +61,14 @@ func main() { func generateSchema() map[string]update.Item { files := []string{ + "cmd/minikube/cmd/config/kubernetes_version.go", "hack/preload-images/kubernetes.go", "hack/update/github.go", "hack/update/ingress_version/update_ingress_version.go", "hack/update/kubeadm_constants/update_kubeadm_constants.go", "hack/update/kubernetes_versions_list/update_kubernetes_versions_list.go", + "hack/update/node_version/update_node_version.go", "pkg/perf/monitor/github.go", - "cmd/minikube/cmd/config/kubernetes_version.go", } schema := make(map[string]update.Item) diff --git a/hack/update/node_version/update_node_version.go b/hack/update/node_version/update_node_version.go new file mode 100644 index 000000000000..a1aa14f12399 --- /dev/null +++ b/hack/update/node_version/update_node_version.go @@ -0,0 +1,93 @@ +/* +Copyright 2024 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/google/go-github/v57/github" + "golang.org/x/mod/semver" + "k8s.io/klog/v2" + "k8s.io/minikube/hack/update" +) + +const ( + // ghListPerPage uses max value (100) for PerPage to avoid hitting the rate limits. + // (ref: https://pkg.go.dev/github.com/google/go-github/github#hdr-Rate_Limiting) + ghListPerPage = 100 + + // ghSearchLimit limits the number of searched items to be <= N * ghListPerPage. + ghSearchLimit = 300 +) + +var schema = map[string]update.Item{ + "netlify.toml": { + Replace: map[string]string{ + `NODE_VERSION = ".*`: `NODE_VERSION = "{{.Version}}"`, + }, + }, +} + +type Data struct { + Version string +} + +func main() { + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) + defer cancel() + + version, err := latestNodeVersionByMajor(ctx, "v20") + if err != nil { + klog.Fatalf("Unable to get stable version: %v", err) + } + version = strings.TrimPrefix(version, "v") + + data := Data{Version: version} + + update.Apply(schema, data) +} + +func latestNodeVersionByMajor(ctx context.Context, major string) (string, error) { + ghc := github.NewClient(nil) + + // walk through the paginated list of up to ghSearchLimit newest releases + opts := &github.ListOptions{PerPage: ghListPerPage} + for (opts.Page+1)*ghListPerPage <= ghSearchLimit { + rls, resp, err := ghc.Repositories.ListTags(ctx, "nodejs", "node", opts) + if err != nil { + return "", err + } + for _, rl := range rls { + ver := rl.GetName() + if !semver.IsValid(ver) { + continue + } + if semver.Major(ver) == major { + return ver, nil + } + } + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + + return "", fmt.Errorf("failed to find a version matching the provided major version %q", major) +}