Skip to content

Commit 7767154

Browse files
Prucekclaude
andcommitted
feat: inject bootstrap jobs for unonboarded repos on PR
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ef439c6 commit 7767154

17 files changed

Lines changed: 1009 additions & 767 deletions

File tree

cmd/ci-operator-prowgen/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import (
88
"path/filepath"
99
"strings"
1010

11-
"sigs.k8s.io/yaml"
1211
"github.com/sirupsen/logrus"
12+
"sigs.k8s.io/yaml"
1313

1414
prowconfig "sigs.k8s.io/prow/pkg/config"
1515
"sigs.k8s.io/prow/pkg/flagutil"
@@ -134,7 +134,7 @@ func (o *options) generateJobsFromFile() error {
134134
configSpec = resolved
135135
}
136136
configSpec.UnresolvedConfigPath = o.fromFile
137-
generated, err := prowgen.GenerateJobs(&configSpec, &info)
137+
generated, err := prowgen.GenerateJobs(&configSpec, &info, nil)
138138
if err != nil {
139139
return err
140140
}

cmd/in-repo-config-plugin/bootstrap.go

Lines changed: 0 additions & 95 deletions
This file was deleted.
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"embed"
6+
"fmt"
7+
"strings"
8+
"text/template"
9+
10+
"github.com/sirupsen/logrus"
11+
"sigs.k8s.io/yaml"
12+
13+
prowconfig "sigs.k8s.io/prow/pkg/config"
14+
15+
cioperatorapi "github.com/openshift/ci-tools/pkg/api"
16+
"github.com/openshift/ci-tools/pkg/config"
17+
jc "github.com/openshift/ci-tools/pkg/jobconfig"
18+
"github.com/openshift/ci-tools/pkg/prowgen"
19+
)
20+
21+
const (
22+
releaseRepoOrg = "openshift"
23+
releaseRepoName = "release"
24+
clusterProfilesCfg = "cluster-profiles/cluster-profiles-config.yaml"
25+
)
26+
27+
//go:embed templates
28+
var templateFS embed.FS
29+
30+
var bootstrapTemplates = template.Must(
31+
template.ParseFS(templateFS, "templates/*.yaml.tmpl"),
32+
)
33+
34+
// --- Config fetching ---
35+
36+
func (s *server) generateAllJobs(org, repo, branch, sha string, logger *logrus.Entry) (*prowconfig.JobConfig, bool, error) {
37+
configs, useDir, err := s.fetchConfigs(org, repo, sha, logger)
38+
if err != nil {
39+
return nil, false, err
40+
}
41+
if len(configs) == 0 {
42+
return nil, false, nil
43+
}
44+
45+
allJobs := &prowconfig.JobConfig{
46+
PresubmitsStatic: map[string][]prowconfig.Presubmit{},
47+
PostsubmitsStatic: map[string][]prowconfig.Postsubmit{},
48+
}
49+
for filename, configSpec := range configs {
50+
info := metadataFromFilename(filename, org, repo, branch)
51+
configSpec.UnresolvedConfigPath = cioperatorapi.CIOperatorInrepoConfigFileName
52+
generated, err := prowgen.GenerateJobs(configSpec, info, nil)
53+
if err != nil {
54+
return nil, false, fmt.Errorf("prowgen failed for %s: %w", filename, err)
55+
}
56+
jc.Append(allJobs, generated)
57+
}
58+
return allJobs, useDir, nil
59+
}
60+
61+
func (s *server) fetchConfigs(org, repo, sha string, l *logrus.Entry) (map[string]*cioperatorapi.ReleaseBuildConfiguration, bool, error) {
62+
entries, err := s.ghc.GetDirectory(org, repo, ciOperatorDir, sha)
63+
if err != nil {
64+
configs, err := s.fetchSingleConfig(org, repo, sha)
65+
return configs, false, err
66+
}
67+
68+
configs := map[string]*cioperatorapi.ReleaseBuildConfiguration{}
69+
for _, entry := range entries {
70+
if entry.Type != "file" {
71+
continue
72+
}
73+
if !strings.HasSuffix(entry.Name, ".yaml") && !strings.HasSuffix(entry.Name, ".yml") {
74+
continue
75+
}
76+
if !strings.HasPrefix(entry.Name, "ci-operator") {
77+
continue
78+
}
79+
80+
content, err := s.ghc.GetFile(org, repo, entry.Path, sha)
81+
if err != nil {
82+
return nil, false, fmt.Errorf("could not fetch %s: %w", entry.Path, err)
83+
}
84+
if content == nil {
85+
l.WithField("file", entry.Path).Warn("file not found")
86+
continue
87+
}
88+
89+
var cfg cioperatorapi.ReleaseBuildConfiguration
90+
if err := yaml.Unmarshal(content, &cfg); err != nil {
91+
return nil, false, fmt.Errorf("could not parse %s: %w", entry.Path, err)
92+
}
93+
configs[entry.Name] = &cfg
94+
}
95+
return configs, true, nil
96+
}
97+
98+
func (s *server) fetchSingleConfig(org, repo, sha string) (map[string]*cioperatorapi.ReleaseBuildConfiguration, error) {
99+
const singleFile = ".ci-operator.yaml"
100+
content, err := s.ghc.GetFile(org, repo, singleFile, sha)
101+
if err != nil {
102+
return nil, fmt.Errorf("could not fetch %s: %w", singleFile, err)
103+
}
104+
if content == nil {
105+
return nil, nil
106+
}
107+
108+
var cfg cioperatorapi.ReleaseBuildConfiguration
109+
if err := yaml.Unmarshal(content, &cfg); err != nil {
110+
return nil, fmt.Errorf("could not parse %s: %w", singleFile, err)
111+
}
112+
return map[string]*cioperatorapi.ReleaseBuildConfiguration{
113+
"ci-operator.yaml": &cfg,
114+
}, nil
115+
}
116+
117+
// --- Bootstrap job generation ---
118+
119+
type bootstrapParams struct {
120+
Org string
121+
Repo string
122+
Branch string
123+
BranchRegex string
124+
UseDir bool
125+
CheckconfigImage string
126+
ProwgenImage string
127+
ConfigDirPath string
128+
ConfigFilePath string
129+
RegistryPath string
130+
ProfilesPath string
131+
}
132+
133+
func newBootstrapParams(org, repo, branch string, useDir bool, prowgenImage, checkconfigImage string) bootstrapParams {
134+
repoPath := fmt.Sprintf("/home/prow/go/src/github.com/%s/%s", org, repo)
135+
releasePath := fmt.Sprintf("/home/prow/go/src/github.com/%s/%s", releaseRepoOrg, releaseRepoName)
136+
137+
return bootstrapParams{
138+
Org: org,
139+
Repo: repo,
140+
Branch: branch,
141+
BranchRegex: jc.ExactlyBranch(branch),
142+
UseDir: useDir,
143+
CheckconfigImage: checkconfigImage,
144+
ProwgenImage: prowgenImage,
145+
ConfigDirPath: fmt.Sprintf("%s/%s", repoPath, ciOperatorDir),
146+
ConfigFilePath: fmt.Sprintf("%s/.ci-operator.yaml", repoPath),
147+
RegistryPath: fmt.Sprintf("%s/%s", releasePath, config.RegistryPath),
148+
ProfilesPath: fmt.Sprintf("%s/%s/%s", releasePath, config.RegistryPath, clusterProfilesCfg),
149+
}
150+
}
151+
152+
func generateBootstrapJobs(params bootstrapParams) (*prowconfig.JobConfig, error) {
153+
orgrepo := fmt.Sprintf("%s/%s", params.Org, params.Repo)
154+
155+
var presubmit prowconfig.Presubmit
156+
if err := renderTemplate("config-check-presubmit.yaml.tmpl", params, &presubmit); err != nil {
157+
return nil, fmt.Errorf("could not render config-check presubmit template: %w", err)
158+
}
159+
160+
var postsubmit prowconfig.Postsubmit
161+
if err := renderTemplate("prowgen-postsubmit.yaml.tmpl", params, &postsubmit); err != nil {
162+
return nil, fmt.Errorf("could not render prowgen postsubmit template: %w", err)
163+
}
164+
165+
return &prowconfig.JobConfig{
166+
PresubmitsStatic: map[string][]prowconfig.Presubmit{
167+
orgrepo: {presubmit},
168+
},
169+
PostsubmitsStatic: map[string][]prowconfig.Postsubmit{
170+
orgrepo: {postsubmit},
171+
},
172+
}, nil
173+
}
174+
175+
func renderTemplate(name string, params bootstrapParams, out any) error {
176+
var buf bytes.Buffer
177+
if err := bootstrapTemplates.ExecuteTemplate(&buf, name, params); err != nil {
178+
return fmt.Errorf("could not execute template %s: %w", name, err)
179+
}
180+
if err := yaml.Unmarshal(buf.Bytes(), out); err != nil {
181+
return fmt.Errorf("could not unmarshal rendered template %s: %w", name, err)
182+
}
183+
return nil
184+
}
File renamed without changes.

0 commit comments

Comments
 (0)