diff --git a/.gitignore b/.gitignore index f168ab3a7..459447af1 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ /openshift /openshift-knative /konflux-gen/out +_output/ # TODO: Remove rpms.lock.yaml diff --git a/config/serverless-operator.yaml b/config/serverless-operator.yaml index ff44644fb..654b82d96 100644 --- a/config/serverless-operator.yaml +++ b/config/serverless-operator.yaml @@ -38,6 +38,7 @@ config: version: "4.14" promotion: {} release-1.35: + golangVersion: "1.24" konflux: enabled: true excludes: diff --git a/pkg/dockerfilegen/generator.go b/pkg/dockerfilegen/generator.go index 97b790896..bfc5d36a1 100644 --- a/pkg/dockerfilegen/generator.go +++ b/pkg/dockerfilegen/generator.go @@ -17,6 +17,7 @@ import ( "strings" "text/template" + "github.com/openshift-knative/hack/config" "github.com/openshift-knative/hack/pkg/soversion" "github.com/coreos/go-semver/semver" @@ -150,12 +151,6 @@ func generateDockerfile(params Params, mainPackagesPaths sets.Set[string]) error if err != nil { return err } - goVersion := goMod.Go.Version - - // The builder images are distinguished by golang major.minor, so we ignore the rest of the goVersion - if strings.Count(goVersion, ".") > 1 { - goVersion = strings.Join(strings.Split(goVersion, ".")[0:2], ".") - } metadata, err := project.ReadMetadataFile(params.ProjectFilePath) if err != nil { @@ -167,6 +162,11 @@ func generateDockerfile(params Params, mainPackagesPaths sets.Set[string]) error metadata = project.DefaultMetadata() } + var goVersion string + if goVersion, err = resolveGolangVersion(params, goMod, metadata); err != nil { + return err + } + rhelVersion := RHEL9 templateFilePattern := "dockerfile-templates/rhel-9/*.tmpl" var soVersion string @@ -400,6 +400,84 @@ func generateDockerfile(params Params, mainPackagesPaths sets.Set[string]) error return nil } +func resolveGolangVersion(params Params, goMod *modfile.File, metadata *project.Metadata) (string, error) { + goVersion := goMod.Go.Version + + // The builder images are distinguished by golang major.minor, so we ignore the rest of the goVersion + if strings.Count(goVersion, ".") > 1 { + goVersion = strings.Join(strings.Split(goVersion, ".")[0:2], ".") + } + + log.Println("Golang version (from go.mod):", goVersion) + + branch := strings.Replace(metadata.Project.Tag, "knative-", "release-", 1) + nvBranch := strings.Replace(branch, "release-v", "release-", 1) + soVer, err := soversion.SoFromUpstreamVersion(nvBranch) + reponame := params.GetRepoName(goMod) + + resolver := golangResolver{ + defaultVersion: goVersion, + params: []golangResolverParams{ + {reponame, branch}, + {reponame, nvBranch}, + }, + } + + if err == nil { + soRelease := soversion.BranchName(soVer) + resolver.params = append(resolver.params, golangResolverParams{ + "serverless-operator", soRelease, + }) + } + + return resolver.perform() +} + +type golangResolverParams struct { + reponame, branch string +} + +type golangResolver struct { + defaultVersion string + params []golangResolverParams +} + +func (r golangResolver) perform() (string, error) { + goVersion := r.defaultVersion + for _, check := range r.params { + configGoVersion, err := resolveGolangVersionForRepo(check.reponame, check.branch) + if err != nil && !errors.Is(err, ErrCantFindConfig) { + return "", err + } + if configGoVersion != "" { + goVersion = configGoVersion + log.Println("Golang version (overridden for", check.reponame, "@", check.branch, "):", goVersion) + break + } + } + return goVersion, nil +} + +var ErrCantFindConfig = errors.New("can't find config file") + +func resolveGolangVersionForRepo(reponame string, branchName string) (string, error) { + cfgYaml, err := config.Configs.ReadFile(fmt.Sprint(reponame, ".yaml")) + if err != nil { + return "", fmt.Errorf("%w: %w", ErrCantFindConfig, err) + } + prowcfg, perr := prowgen.UnmarshalConfig(cfgYaml) + if perr != nil { + return "", errors.WithStack(perr) + } + + branch := prowcfg.Config.Branches[branchName] + if branch.GolangVersion != "" { + goVersion := branch.GolangVersion + return goVersion, nil + } + return "", nil +} + func hasVendorFolder(dir string) (bool, error) { info, err := os.Stat(path.Join(dir, "vendor")) if err == nil { diff --git a/pkg/dockerfilegen/params.go b/pkg/dockerfilegen/params.go index 5a3e1119c..d214dad0e 100644 --- a/pkg/dockerfilegen/params.go +++ b/pkg/dockerfilegen/params.go @@ -7,6 +7,7 @@ import ( "github.com/octago/sflags" "github.com/octago/sflags/gen/gpflag" "github.com/spf13/pflag" + "golang.org/x/mod/modfile" ) type Params struct { @@ -20,6 +21,7 @@ type Params struct { DockerfilesBuildDir string `json:"dockerfiles-build-dir" desc:"Dockerfiles output directory for build image relative to output flag"` DockerfilesSourceDir string `json:"dockerfiles-source-dir" desc:"Dockerfiles output directory for source image relative to output flag"` ProjectFilePath string `json:"project-file" desc:"Project metadata file path"` + RepoName string `json:"repo-name" desc:"Repository name (implied from go.mod by default)"` DockerfileImageBuilderFmt string `json:"dockerfile-image-builder-fmt" desc:"Dockerfile image builder format"` AppFileFmt string `json:"app-file-fmt" desc:"Target application binary path format"` RegistryImageFmt string `json:"registry-image-fmt" desc:"Container registry image format"` @@ -40,6 +42,13 @@ func (p *Params) ConfigureFlags() (*pflag.FlagSet, error) { return fs, nil } +func (p *Params) GetRepoName(goMod *modfile.File) string { + if p.RepoName != "" { + return p.RepoName + } + return path.Base(goMod.Module.Mod.Path) +} + func DefaultParams(wd string) Params { return Params{ RootDir: wd, diff --git a/pkg/prowgen/prowgen_config.go b/pkg/prowgen/prowgen_config.go index 5350c89ea..612410b58 100644 --- a/pkg/prowgen/prowgen_config.go +++ b/pkg/prowgen/prowgen_config.go @@ -91,6 +91,7 @@ type Branch struct { SkipE2EMatches []string `json:"skipE2EMatches,omitempty" yaml:"skipE2EMatches,omitempty"` SkipDockerFilesMatches []string `json:"skipDockerFilesMatches,omitempty" yaml:"skipDockerFilesMatches,omitempty"` Konflux *Konflux `json:"konflux,omitempty" yaml:"konflux,omitempty"` + GolangVersion string `json:"golangVersion,omitempty" yaml:"golangVersion,omitempty"` // DependabotEnabled enabled if `nil`. DependabotEnabled *bool `json:"dependabotEnabled,omitempty" yaml:"dependabotEnabled,omitempty"` diff --git a/pkg/soversion/version.go b/pkg/soversion/version.go index 094b18882..5260e8064 100644 --- a/pkg/soversion/version.go +++ b/pkg/soversion/version.go @@ -5,9 +5,18 @@ import ( "strings" "github.com/coreos/go-semver/semver" + "github.com/pkg/errors" ) func FromUpstreamVersion(upstream string) *semver.Version { + ver, err := SoFromUpstreamVersion(upstream) + if err != nil { + panic(fmt.Sprintf("%+v", err)) + } + return ver +} + +func SoFromUpstreamVersion(upstream string) (*semver.Version, error) { upstream = strings.Replace(upstream, "release-v", "", 1) upstream = strings.Replace(upstream, "release-", "", 1) upstream = strings.Replace(upstream, "v", "", 1) @@ -16,7 +25,10 @@ func FromUpstreamVersion(upstream string) *semver.Version { if len(dotParts) == 2 { upstream = upstream + ".0" } - soVersion := semver.New(upstream) + soVersion, err := semver.NewVersion(upstream) + if err != nil { + return nil, errors.WithStack(err) + } for i := 0; i < 21; i++ { // Example 1.11 -> 1.32 soVersion.BumpMinor() } @@ -29,7 +41,7 @@ func FromUpstreamVersion(upstream string) *semver.Version { soVersion.Minor -= 1 } - return soVersion + return soVersion, nil } func ToUpstreamVersion(soversion string) *semver.Version {