mirror of https://github.com/knative/func.git
drastically simplify the components update
- now runs in update-deps.sh - only need to modify json and autogen script
This commit is contained in:
parent
719b2863ca
commit
c7cd47110e
|
@ -27,5 +27,5 @@ jobs:
|
|||
"$HOME/.config/containers/registries.conf"
|
||||
skopeo login ghcr.io -u gh-action -p "$GITHUB_TOKEN"
|
||||
docker login ghcr.io -u gh-action -p "$GITHUB_TOKEN"
|
||||
make wf-update-builder
|
||||
|
||||
make __update-builder
|
||||
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
name: Update Kn components in hack/ scripts
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# every 4 hours
|
||||
- cron: '0 */4 * * *'
|
||||
jobs:
|
||||
update:
|
||||
name: Update components
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.23.0
|
||||
- name: Run script
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
run: make wf-generate-kn-components
|
||||
|
17
Makefile
17
Makefile
|
@ -308,23 +308,16 @@ schema-check: ## Check that func.yaml schema is up-to-date
|
|||
##@ Hack scripting
|
||||
######################
|
||||
|
||||
### Local section - Can be run locally!
|
||||
|
||||
.PHONY: generate-kn-components-local
|
||||
generate-kn-components-local: ## Generate knative components locally
|
||||
cd hack && go run ./cmd/update-knative-components "local"
|
||||
.PHONY: hack-generate-components
|
||||
hack-generate-components: ## Regenerate components in hack/ dir
|
||||
cd hack && go run ./cmd/components
|
||||
|
||||
.PHONY: test-hack
|
||||
test-hack:
|
||||
cd hack && go test ./... -v
|
||||
|
||||
### Automated section - This gets run in workflows, scripts etc.
|
||||
.PHONY: wf-generate-kn-components
|
||||
wf-generate-kn-components: # Generate kn components - used in automation
|
||||
cd hack && go run ./cmd/update-knative-components
|
||||
## This is used by workflows
|
||||
|
||||
.PHONY: update-builder
|
||||
wf-update-builder: # Used in automation
|
||||
__update-builder: # Used in automation
|
||||
cd hack && go run ./cmd/update-builder
|
||||
|
||||
### end of automation section
|
||||
|
|
|
@ -0,0 +1,244 @@
|
|||
// Package main implements a tool for automatically updating component
|
||||
// versions for use in the hack/* scripts.
|
||||
//
|
||||
// Files interacted with:
|
||||
// 1. The source-of-truth file at hack/component-versions.json
|
||||
// 2. Autogenerated script at hack/component-versions.sh
|
||||
//
|
||||
// USAGE:
|
||||
//
|
||||
// This is running on semi-auto basis where versions are being auto bumped via
|
||||
// PRs sent to main. Semi-auto because if repo is missing 'owner' or 'repo' fields
|
||||
// in the .json it will not be automatically bumped (it has no repo to look).
|
||||
// This is intentional for components we dont want autobumped like this.
|
||||
// The source-of-truth file is found in this repo @root/hack/component-versions.json
|
||||
//
|
||||
// ADD NEW/MODIFY COMPONENTS
|
||||
//
|
||||
// 1. Edit source-of-truth .json file
|
||||
// ! If a component is missing "owner" or "repo" it will not be auto-bumped
|
||||
// 2. If new component was added:
|
||||
// - Edit the autogenerated text just below here 'versionsScriptTemplate'
|
||||
// 3. Regenerate using Makefile - find target 'hack-generate-components'
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
github "github.com/google/go-github/v68/github"
|
||||
)
|
||||
|
||||
const (
|
||||
fileScript string = "component-versions.sh"
|
||||
fileJson string = "component-versions.json"
|
||||
|
||||
versionsScriptTemplate string = `#!/usr/bin/env bash
|
||||
|
||||
# AUTOGENERATED FILE - edit versions in ./component-versions.json.
|
||||
# If you want to add/modify these components, please read the how-to steps in
|
||||
# ./cmd/components/main.go.
|
||||
# You can regenerate with "make hack-generate-components".
|
||||
|
||||
set_versions() {
|
||||
# Note: Kubernetes Version node image per Kind releases (full hash is suggested):
|
||||
# https://github.com/kubernetes-sigs/kind/releases
|
||||
kind_node_version={{.KindNode.Version}}
|
||||
|
||||
# find source-of-truth in component-versions.json to add/modify components
|
||||
knative_serving_version="{{.Serving.Version}}"
|
||||
knative_eventing_version="{{.Eventing.Version}}"
|
||||
contour_version="{{.Contour.Version}}"
|
||||
tekton_version="{{.Tekton.Version}}"
|
||||
pac_version="{{.Pac.Version}}"
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
// Individual component info like for "Serving" or "Eventing"
|
||||
// If you want to add new component, read the comment at the top of the file!
|
||||
type Component struct {
|
||||
Version string `json:"version"`
|
||||
Owner string `json:"owner,omitempty"`
|
||||
Repo string `json:"repo,omitempty"`
|
||||
}
|
||||
|
||||
// make iterable struct
|
||||
type ComponentList map[string]*Component
|
||||
|
||||
func main() {
|
||||
// Set up context for possible signal inputs to not disrupt cleanup process.
|
||||
// This is not gonna do much for workflows since they finish and shutdown
|
||||
// but in case of local testing - dont leave left over resources on disk/RAM.
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
go func() {
|
||||
<-sigs
|
||||
cancel()
|
||||
<-sigs
|
||||
os.Exit(130)
|
||||
}()
|
||||
|
||||
getClient := func(token string) *github.Client {
|
||||
if token != "" {
|
||||
fmt.Println("client with token")
|
||||
return github.NewClient(nil).WithAuthToken(token)
|
||||
}
|
||||
return github.NewClient(nil)
|
||||
}
|
||||
client := getClient(os.Getenv("GITHUB_TOKEN"))
|
||||
|
||||
// Read source-of-truth .json
|
||||
componentList, err := readVersions(fileJson)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// update componentList in-situ
|
||||
updated, err := update(ctx, client, &componentList)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to update %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !updated {
|
||||
// nothing was updated, nothing to do
|
||||
fmt.Println("no newer versions found, re-generating .sh just in case")
|
||||
// regenerate .sh to keep up to date if changed
|
||||
err = writeScript(componentList, fileScript)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to re-generate script: %v", err)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("all good")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if err := writeFiles(componentList, fileScript, fileJson); err != nil {
|
||||
err = fmt.Errorf("failed to write files: %v", err)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Println("files updated!")
|
||||
}
|
||||
|
||||
// do the update for each repo defined
|
||||
func update(ctx context.Context, client *github.Client, cl *ComponentList) (bool, error) {
|
||||
fmt.Println("Getting latest releases")
|
||||
updated := false
|
||||
for _, c := range *cl {
|
||||
if c.Owner == "" || c.Repo == "" {
|
||||
//skipping auto updates
|
||||
continue
|
||||
}
|
||||
|
||||
newV, err := getLatestVersion(ctx, client, c.Owner, c.Repo)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error while getting latest v of %s/%s: %v", c.Owner, c.Repo, err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
if c.Version != newV {
|
||||
fmt.Printf("bump %v: %v --> %v\n", fmt.Sprintf("%s/%s", c.Owner, c.Repo), c.Version, newV)
|
||||
c.Version = newV
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
return updated, nil
|
||||
}
|
||||
|
||||
// read (unmarshal) component versions from .json
|
||||
func readVersions(file string) (c ComponentList, err error) {
|
||||
fmt.Println("Reading versions from source-of-truth")
|
||||
data, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(data, &c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Overwrite the 'source of truth' file - .json and regenerate new script
|
||||
// with new versions from 'v'.
|
||||
// Arguments 'script' & 'json' are paths to files for autogenerated script and
|
||||
// source (json) file respectively.
|
||||
func writeFiles(cl ComponentList, script, json string) error {
|
||||
fmt.Print("Writing files")
|
||||
// write to json
|
||||
err := writeSource(cl, json)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write to json: %v", err)
|
||||
}
|
||||
// write to script file
|
||||
err = writeScript(cl, script)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate script: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// write to 'source of truth' .json with updated versions (if pulled latest)
|
||||
func writeSource(cl ComponentList, file string) error {
|
||||
vB, err := json.MarshalIndent(cl, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("cant Marshal versions: %v", err)
|
||||
}
|
||||
f, err := os.Create(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
_, err = f.Write(append(vB, '\n')) // append newline for reviewdog
|
||||
return err
|
||||
}
|
||||
|
||||
// write the autogenerated script based on 'cl'
|
||||
func writeScript(cl ComponentList, file string) error {
|
||||
tmpl, err := template.New("versions").Parse(versionsScriptTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.Create(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err := tmpl.Execute(f, cl); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// get latest version of owner/repo via GH API
|
||||
func getLatestVersion(ctx context.Context, client *github.Client, owner string, repo string) (v string, err error) {
|
||||
rr, res, err := client.Repositories.GetLatestRelease(ctx, owner, repo)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: request for latest %s release: %v", owner+"/"+repo, err)
|
||||
return
|
||||
}
|
||||
if res.StatusCode < 200 && res.StatusCode > 299 {
|
||||
err = fmt.Errorf("error: Return status code of request for latest %s release is %d", owner+"/"+repo, res.StatusCode)
|
||||
return
|
||||
}
|
||||
v = *rr.Name
|
||||
if v == "" {
|
||||
return "", fmt.Errorf("internal error: returned latest release name is empty for '%s'", repo)
|
||||
}
|
||||
return v, nil
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
const expectedScript string = `#!/usr/bin/env bash
|
||||
|
||||
# AUTOGENERATED FILE - edit versions in ./component-versions.json.
|
||||
# If you want to add/modify these components, please read the how-to steps in
|
||||
# ./cmd/components/main.go.
|
||||
# You can regenerate with "make hack-generate-components".
|
||||
|
||||
set_versions() {
|
||||
# Note: Kubernetes Version node image per Kind releases (full hash is suggested):
|
||||
# https://github.com/kubernetes-sigs/kind/releases
|
||||
kind_node_version=v1.32.0@sha256:c48c62eac5da28cdadcf560d1d8616cfa6783b58f0d94cf63ad1bf49600cb027
|
||||
|
||||
# find source-of-truth in component-versions.json to add/modify components
|
||||
knative_serving_version="v1.19.0"
|
||||
knative_eventing_version="v1.19.0"
|
||||
contour_version="v1.19.0"
|
||||
tekton_version="v1.1.0"
|
||||
pac_version="v0.35.2"
|
||||
}
|
||||
`
|
||||
|
||||
const expectedJson string = `{
|
||||
"Contour": {
|
||||
"version": "v1.19.0",
|
||||
"owner": "knative-extensions",
|
||||
"repo": "net-contour"
|
||||
},
|
||||
"Eventing": {
|
||||
"version": "v1.19.0",
|
||||
"owner": "knative",
|
||||
"repo": "eventing"
|
||||
},
|
||||
"KindNode": {
|
||||
"version": "v1.32.0@sha256:c48c62eac5da28cdadcf560d1d8616cfa6783b58f0d94cf63ad1bf49600cb027"
|
||||
},
|
||||
"Pac": {
|
||||
"version": "v0.35.2"
|
||||
},
|
||||
"Serving": {
|
||||
"version": "v1.19.0",
|
||||
"owner": "knative",
|
||||
"repo": "serving"
|
||||
},
|
||||
"Tekton": {
|
||||
"version": "v1.1.0"
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
// TestRead should just successfully unmarshal
|
||||
func TestRead(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
// just assign this for simplicity, no need to the same variable twice
|
||||
const jsonContent = expectedJson
|
||||
|
||||
tmpJson := path.Join(dir, "json.json")
|
||||
err := os.WriteFile(tmpJson, []byte(jsonContent), fs.ModePerm)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = readVersions(tmpJson)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read json: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestWrite ensures that writing both files succeeds with the correct file
|
||||
// contents
|
||||
func TestWrite(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
tmpJson := path.Join(dir, "f.json")
|
||||
tmpSh := path.Join(dir, "f.sh")
|
||||
|
||||
// as if we just unmarshalled this
|
||||
c := ComponentList{
|
||||
"Contour": &Component{
|
||||
Version: "v1.19.0",
|
||||
Owner: "knative-extensions",
|
||||
Repo: "net-contour",
|
||||
},
|
||||
"Eventing": {
|
||||
Version: "v1.19.0",
|
||||
Owner: "knative",
|
||||
Repo: "eventing",
|
||||
},
|
||||
"KindNode": {
|
||||
Version: "v1.32.0@sha256:c48c62eac5da28cdadcf560d1d8616cfa6783b58f0d94cf63ad1bf49600cb027",
|
||||
},
|
||||
"Pac": {
|
||||
Version: "v0.35.2",
|
||||
},
|
||||
"Serving": {
|
||||
Version: "v1.19.0",
|
||||
Owner: "knative",
|
||||
Repo: "serving",
|
||||
},
|
||||
"Tekton": {
|
||||
Version: "v1.1.0",
|
||||
},
|
||||
}
|
||||
|
||||
// write to script (generate it)
|
||||
err := writeScript(c, tmpSh)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate script: %v", err)
|
||||
}
|
||||
// assert
|
||||
fsh, err := os.ReadFile(tmpSh)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read file: %v", err)
|
||||
}
|
||||
assert.Equal(t, string(fsh), expectedScript)
|
||||
|
||||
// write to json
|
||||
err = writeSource(c, tmpJson)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to write json: %v", err)
|
||||
}
|
||||
|
||||
// assert
|
||||
fjson, err := os.ReadFile(tmpJson)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read json: %v", err)
|
||||
}
|
||||
assert.Equal(t, string(fjson), expectedJson)
|
||||
}
|
|
@ -1,384 +0,0 @@
|
|||
// Package main implements a tool for automatically updating component
|
||||
// versions for use in the hack/* scripts.
|
||||
//
|
||||
// Files interacted with:
|
||||
// 1. The source-of-truth file at hack/component-versions.json
|
||||
// 2. Autogenerated script at hack/component-versions.sh
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// Most of the time this file will be used in a workflow that will run
|
||||
// on scheduled basis checking if a new latest version of corresponding
|
||||
// components exists (check components in 'Versions' struct). Please note that
|
||||
// KindNode is NOT being updated programmatically at this time.
|
||||
// When new latest version is detected, the program will create a PR in
|
||||
// knative/func repository with the latest changes allowing the CI/CD workflows
|
||||
// to run automatically before using the latest in main branch.
|
||||
//
|
||||
// Alternative use: You can run this file from hack/ directory to locally
|
||||
// regenerate 2 files mentioned above (if you made some changes etc.) - you can
|
||||
// use the root Makefile for your convenience -- 'make regenerate-kn-components'
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
github "github.com/google/go-github/v68/github"
|
||||
)
|
||||
|
||||
const (
|
||||
fileScript string = "component-versions.sh"
|
||||
fileJson string = "component-versions.json"
|
||||
|
||||
versionsScriptTemplate string = `#!/usr/bin/env bash
|
||||
|
||||
# AUTOGENERATED FILE - edit versions in ./component-versions.json.
|
||||
# If you are adding components, modify this scripts' template in
|
||||
# ./cmd/update-knative-components/main.go.
|
||||
# You can regenerate locally with "make generate-kn-components-local".
|
||||
|
||||
set_versions() {
|
||||
# Note: Kubernetes Version node image per Kind releases (full hash is suggested):
|
||||
# https://github.com/kubernetes-sigs/kind/releases
|
||||
kind_node_version={{.KindNode}}
|
||||
|
||||
# gets updated programatically via workflow -> PR creation
|
||||
knative_serving_version="{{.Serving}}"
|
||||
knative_eventing_version="{{.Eventing}}"
|
||||
contour_version="{{.Contour}}"
|
||||
tekton_version="{{.Tekton}}"
|
||||
pac_version="{{.Pac}}"
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
// all the components that are kept up to date
|
||||
type Versions struct {
|
||||
KindNode string
|
||||
Serving string
|
||||
Eventing string
|
||||
Contour string
|
||||
Tekton string
|
||||
Pac string
|
||||
}
|
||||
|
||||
func main() {
|
||||
// there is an optional "local" argument
|
||||
if len(os.Args) == 2 && os.Args[1] == "local" { //leveraging lazy evals
|
||||
fmt.Println("Generate argument received! Regenerating files locally...")
|
||||
err := generateComponentVersions()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("done")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Set up context for possible signal inputs to not disrupt cleanup process.
|
||||
// This is not gonna do much for workflows since they finish and shutdown
|
||||
// but in case of local testing - dont leave left over resources on disk/RAM.
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
go func() {
|
||||
<-sigs
|
||||
cancel()
|
||||
<-sigs
|
||||
os.Exit(130)
|
||||
}()
|
||||
|
||||
const prTitle = "chore: Update components' versions to latest"
|
||||
client := github.NewClient(nil).WithAuthToken(os.Getenv("GITHUB_TOKEN"))
|
||||
|
||||
e, err := prExists(ctx, client, prTitle)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if e {
|
||||
fmt.Printf("PR already exists, nothing to do, exiting")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
projects := []struct {
|
||||
owner, repo string
|
||||
}{
|
||||
{
|
||||
owner: "knative",
|
||||
repo: "serving",
|
||||
},
|
||||
{
|
||||
owner: "knative",
|
||||
repo: "eventing",
|
||||
},
|
||||
{
|
||||
owner: "knative-extensions",
|
||||
repo: "net-contour",
|
||||
},
|
||||
}
|
||||
|
||||
// Get current versions used.
|
||||
v, err := readVersions(fileJson)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
updated := false
|
||||
// cycle through all versions of components listed above, fetch their
|
||||
// latest from github releases - cmp them - create PR for update if necessary
|
||||
for _, p := range projects {
|
||||
newV, err := getLatestVersion(ctx, client, p.owner, p.repo)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error while getting latest v of %s/%s: %v", p.owner, p.repo, err)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// try to overwrite with possibly new versions
|
||||
if updateVersion(&v, p.repo, newV) {
|
||||
// if any of the files are updated, set true
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
|
||||
if !updated {
|
||||
// nothing was updated, nothing to do
|
||||
fmt.Printf("all good, no newer component releases, exiting\n")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if err := writeFiles(v, fileScript, fileJson); err != nil {
|
||||
err = fmt.Errorf("failed to write files: %v", err)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Println("files updated!")
|
||||
|
||||
branchName := "update-components" + time.Now().Format(time.DateOnly)
|
||||
err = prepareBranch(branchName)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to prep the branch: %v", err)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = createPR(ctx, client, prTitle, branchName)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// read (unmarshal) component versions from .json
|
||||
func readVersions(file string) (v Versions, err error) {
|
||||
fmt.Print("> Reading versions from file...")
|
||||
data, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(data, &v)
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
fmt.Println("done")
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// attempt to update 'v' Versions to new 'val' for specific 'repo'
|
||||
// 'v' - structure that holds versions of components
|
||||
// 'repo' - points to a component repo that is being updated (example: serving)
|
||||
// 'val' - value of the latest release of that repo (pulled via GH API)
|
||||
func updateVersion(v *Versions, repo, val string) (updated bool) {
|
||||
fmt.Printf("check for %s update...", repo)
|
||||
if repo == "serving" && v.Serving != val {
|
||||
v.Serving = val
|
||||
updated = true
|
||||
} else if repo == "eventing" && v.Eventing != val {
|
||||
v.Eventing = val
|
||||
updated = true
|
||||
} else if repo == "net-contour" && v.Contour != val {
|
||||
v.Contour = val
|
||||
updated = true
|
||||
}
|
||||
if updated {
|
||||
fmt.Printf("found! new:'%s'\n", val)
|
||||
} else {
|
||||
fmt.Println("nothing to do")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Overwrite the 'source of truth' file - .json and regenerate new script
|
||||
// with new versions from 'v'.
|
||||
// Arguments 'script' & 'json' are paths to files for autogenerated script and
|
||||
// source (json) file respectively.
|
||||
func writeFiles(v Versions, script, json string) error {
|
||||
fmt.Print("> Writing into files...")
|
||||
// write to json
|
||||
err := writeVersionsSource(v, json)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write to json: %v", err)
|
||||
}
|
||||
// write to script file
|
||||
err = writeVersionsScript(v, script)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate script: %v", err)
|
||||
}
|
||||
fmt.Println("done")
|
||||
return nil
|
||||
}
|
||||
|
||||
// write to 'source of truth' file (json) with updated versions
|
||||
func writeVersionsSource(v Versions, file string) error {
|
||||
vB, err := json.MarshalIndent(v, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("cant Marshal versions: %v", err)
|
||||
}
|
||||
f, err := os.Create(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
_, err = f.Write(append(vB, '\n')) // append newline for reviewdog
|
||||
return err
|
||||
}
|
||||
|
||||
// write to autogenerated script file with newest Versions via templates pkg
|
||||
func writeVersionsScript(v Versions, file string) error {
|
||||
tmpl, err := template.New("versions").Parse(versionsScriptTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.Create(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err := tmpl.Execute(f, v); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// get latest version of owner/repo via GH API
|
||||
func getLatestVersion(ctx context.Context, client *github.Client, owner string, repo string) (v string, err error) {
|
||||
fmt.Printf("> get latest '%s/%s'...", owner, repo)
|
||||
rr, res, err := client.Repositories.GetLatestRelease(ctx, owner, repo)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: request for latest %s release: %v", owner+"/"+repo, err)
|
||||
return
|
||||
}
|
||||
if res.StatusCode < 200 && res.StatusCode > 299 {
|
||||
err = fmt.Errorf("error: Return status code of request for latest %s release is %d", owner+"/"+repo, res.StatusCode)
|
||||
return
|
||||
}
|
||||
v = *rr.Name
|
||||
if v == "" {
|
||||
return "", fmt.Errorf("internal error: returned latest release name is empty for '%s'", repo)
|
||||
}
|
||||
fmt.Println("done")
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// prepare branch for PR via git commands
|
||||
// config user as knative bot -> create branch -> add changes -> commit -> push
|
||||
func prepareBranch(branchName string) error {
|
||||
fmt.Println("> preparing branch")
|
||||
cmd := exec.Command("bash", "-c", fmt.Sprintf(`
|
||||
git config --local user.email "automation@knative.team" &&
|
||||
git config --local user.name "Knative Automation" &&
|
||||
git switch -c %s &&
|
||||
git add %s %s &&
|
||||
git commit -m "update components" &&
|
||||
git push origin %s
|
||||
`, branchName, fileScript, fileJson, branchName))
|
||||
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdout = os.Stdout
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// create a PR via GH API on the func repo with those new versions
|
||||
func createPR(ctx context.Context, client *github.Client, title string, branchName string) error {
|
||||
fmt.Print("> creating PR...")
|
||||
bodyText := "You might need to close & open this PR so all tests can run"
|
||||
body := fmt.Sprintf("%s\n%s\n/assign @gauron99", title, bodyText)
|
||||
|
||||
newPR := github.NewPullRequest{
|
||||
Title: github.Ptr(title),
|
||||
Base: github.Ptr("main"),
|
||||
Head: github.Ptr(branchName),
|
||||
Body: github.Ptr(body),
|
||||
MaintainerCanModify: github.Ptr(true),
|
||||
}
|
||||
pr, _, err := client.PullRequests.Create(ctx, "knative", "func", &newPR)
|
||||
if err != nil {
|
||||
fmt.Printf("PR looks like this:\n%#v\n", pr)
|
||||
fmt.Printf("err: %s\n", err)
|
||||
return err
|
||||
}
|
||||
fmt.Println("ready")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns true when PR with given title already exists in knative/func repo
|
||||
// otherwise false.
|
||||
// Returns an error if occurred, otherwise nil.
|
||||
func prExists(ctx context.Context, c *github.Client, title string) (bool, error) {
|
||||
perPage := 10
|
||||
opt := &github.PullRequestListOptions{State: "open", ListOptions: github.ListOptions{PerPage: perPage}}
|
||||
for {
|
||||
list, resp, err := c.PullRequests.List(ctx, "knative", "func", opt)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("errror pulling PRs in knative/func: %s", err)
|
||||
}
|
||||
for _, pr := range list {
|
||||
if pr.GetTitle() == title {
|
||||
// gauron99 - currently cannot update already existing PR
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
if resp.NextPage == 0 {
|
||||
// hit end of list
|
||||
return false, nil
|
||||
}
|
||||
// otherwise, continue to the next page
|
||||
opt.Page = resp.NextPage
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------- \\
|
||||
// -------------------------------------------------------------------------- \\
|
||||
// -------------------------------------------------------------------------- \\
|
||||
// -------------------------------------------------------------------------- \\
|
||||
|
||||
// This is used when running this file with 1st argument "local".
|
||||
// Regenerate written files (source (.json) & autogenerated .sh file)
|
||||
// Generally you wont use this, but in case you make local changes to the
|
||||
// files, you can simply regenerate them with this
|
||||
func generateComponentVersions() error {
|
||||
v, err := readVersions(fileJson)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read Versions from json: %v", err)
|
||||
}
|
||||
// generate
|
||||
err = writeFiles(v, fileScript, fileJson)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write Versions: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
const expectedScript string = `#!/usr/bin/env bash
|
||||
|
||||
# AUTOGENERATED FILE - edit versions in ./component-versions.json.
|
||||
# If you are adding components, modify this scripts' template in
|
||||
# ./cmd/update-knative-components/main.go.
|
||||
# You can regenerate locally with "make generate-kn-components-local".
|
||||
|
||||
set_versions() {
|
||||
# Note: Kubernetes Version node image per Kind releases (full hash is suggested):
|
||||
# https://github.com/kubernetes-sigs/kind/releases
|
||||
kind_node_version=v1.30
|
||||
|
||||
# gets updated programatically via workflow -> PR creation
|
||||
knative_serving_version="v1.2.3"
|
||||
knative_eventing_version="v1.4.5"
|
||||
contour_version="v1.4.6"
|
||||
tekton_version="v0.56.4"
|
||||
pac_version="v0.24.6"
|
||||
}
|
||||
`
|
||||
|
||||
const expectedJson string = `{
|
||||
"KindNode": "v1.30",
|
||||
"Serving": "v1.2.3",
|
||||
"Eventing": "v1.4.5",
|
||||
"Contour": "v1.4.6",
|
||||
"Tekton": "v0.56.4",
|
||||
"Pac": "v0.24.6"
|
||||
}
|
||||
`
|
||||
|
||||
// TestRead should just successfully unmarshal ('v' struct & json compatibility)
|
||||
func TestRead(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
const jsonContent string = `{
|
||||
"Serving": "v1.14",
|
||||
"Eventing": "v1.15",
|
||||
"Contour": "v1.61",
|
||||
"KindNode": "1.3456",
|
||||
"Tekton": "v0.50.0",
|
||||
"Pac": "v0.20.0"
|
||||
}
|
||||
`
|
||||
tmpJson := path.Join(dir, "json.json")
|
||||
err := os.WriteFile(tmpJson, []byte(jsonContent), fs.ModePerm)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = readVersions(tmpJson)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read json: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestWrite ensures that writing both files succeeds with the correct file
|
||||
// contents
|
||||
func TestWrite(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
tmpJson := path.Join(dir, "f.json")
|
||||
tmpSh := path.Join(dir, "f.sh")
|
||||
|
||||
v := Versions{
|
||||
Serving: "v1.2.3",
|
||||
Eventing: "v1.4.5",
|
||||
Contour: "v1.4.6",
|
||||
KindNode: "v1.30",
|
||||
Tekton: "v0.56.4",
|
||||
Pac: "v0.24.6",
|
||||
}
|
||||
|
||||
// write to script (generate it)
|
||||
err := writeVersionsScript(v, tmpSh)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate script: %v", err)
|
||||
}
|
||||
// assert
|
||||
fsh, err := os.ReadFile(tmpSh)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read file: %v", err)
|
||||
}
|
||||
assert.Equal(t, string(fsh), expectedScript)
|
||||
|
||||
// write to json
|
||||
err = writeVersionsSource(v, tmpJson)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to write json: %v", err)
|
||||
}
|
||||
|
||||
// assert
|
||||
fjson, err := os.ReadFile(tmpJson)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read json: %v", err)
|
||||
}
|
||||
assert.Equal(t, string(fjson), expectedJson)
|
||||
}
|
|
@ -1,8 +1,26 @@
|
|||
{
|
||||
"KindNode": "v1.32.0@sha256:c48c62eac5da28cdadcf560d1d8616cfa6783b58f0d94cf63ad1bf49600cb027",
|
||||
"Serving": "v1.19.0",
|
||||
"Eventing": "v1.19.0",
|
||||
"Contour": "v1.19.0",
|
||||
"Tekton": "v1.1.0",
|
||||
"Pac": "v0.35.2"
|
||||
"Contour": {
|
||||
"version": "v1.19.0",
|
||||
"owner": "knative-extensions",
|
||||
"repo": "net-contour"
|
||||
},
|
||||
"Eventing": {
|
||||
"version": "v1.19.0",
|
||||
"owner": "knative",
|
||||
"repo": "eventing"
|
||||
},
|
||||
"KindNode": {
|
||||
"version": "v1.32.0@sha256:c48c62eac5da28cdadcf560d1d8616cfa6783b58f0d94cf63ad1bf49600cb027"
|
||||
},
|
||||
"Pac": {
|
||||
"version": "v0.35.2"
|
||||
},
|
||||
"Serving": {
|
||||
"version": "v1.19.0",
|
||||
"owner": "knative",
|
||||
"repo": "serving"
|
||||
},
|
||||
"Tekton": {
|
||||
"version": "v1.1.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# AUTOGENERATED FILE - edit versions in ./component-versions.json.
|
||||
# If you are adding components, modify this scripts' template in
|
||||
# ./cmd/update-knative-components/main.go.
|
||||
# You can regenerate locally with "make generate-kn-components-local".
|
||||
# If you want to add/modify these components, please read the how-to steps in
|
||||
# ./cmd/components/main.go.
|
||||
# You can regenerate with "make hack-generate-components".
|
||||
|
||||
set_versions() {
|
||||
# Note: Kubernetes Version node image per Kind releases (full hash is suggested):
|
||||
# https://github.com/kubernetes-sigs/kind/releases
|
||||
kind_node_version=v1.32.0@sha256:c48c62eac5da28cdadcf560d1d8616cfa6783b58f0d94cf63ad1bf49600cb027
|
||||
|
||||
# gets updated programatically via workflow -> PR creation
|
||||
# find source-of-truth in component-versions.json to add/modify components
|
||||
knative_serving_version="v1.19.0"
|
||||
knative_eventing_version="v1.19.0"
|
||||
contour_version="v1.19.0"
|
||||
|
|
|
@ -21,3 +21,6 @@ set -o pipefail
|
|||
source "$(go run knative.dev/hack/cmd/script library.sh)"
|
||||
|
||||
go_update_deps "$@"
|
||||
|
||||
# Update hack components
|
||||
make hack-generate-components
|
||||
|
|
Loading…
Reference in New Issue