diff --git a/releng/release-tool/BUILD.bazel b/releng/release-tool/BUILD.bazel index 496a9528f0..8c08d036bd 100644 --- a/releng/release-tool/BUILD.bazel +++ b/releng/release-tool/BUILD.bazel @@ -8,6 +8,7 @@ go_library( deps = [ "@com_github_google_go_github_v32//github:go_default_library", "@com_github_masterminds_semver//:go_default_library", + "@io_k8s_sigs_yaml//:go_default_library", "@org_golang_x_oauth2//:go_default_library", ], ) @@ -49,5 +50,8 @@ go_test( name = "go_default_test", srcs = ["release-tool_test.go"], embed = [":go_default_library"], - deps = ["@com_github_google_go_github_v32//github:go_default_library"], + deps = [ + "@com_github_google_go_github_v32//github:go_default_library", + "@io_k8s_sigs_yaml//:go_default_library", + ], ) diff --git a/releng/release-tool/release-tool.go b/releng/release-tool/release-tool.go index 4fa5eb9852..c941d45975 100644 --- a/releng/release-tool/release-tool.go +++ b/releng/release-tool/release-tool.go @@ -14,10 +14,10 @@ import ( "strings" "time" - "golang.org/x/oauth2" - "github.com/Masterminds/semver" "github.com/google/go-github/v32/github" + "golang.org/x/oauth2" + "sigs.k8s.io/yaml" ) type blockerListCacheEntry struct { @@ -414,8 +414,14 @@ func (r *releaseData) forkProwJobs() error { // create new prow configs if they don't already exist if _, err := os.Stat(fullOutputConfig); err != nil && os.IsNotExist(err) { + updateJobConfig, err := updatePhase2Jobs(r.org+"/"+r.repo, fullJobConfig) + if err != nil { + return err + } + defer os.Remove(updateJobConfig) + log.Printf("Creating new prow yaml at path %s", fullOutputConfig) - cmd := exec.Command("/usr/bin/config-forker", "--job-config", fullJobConfig, "--version", version, "--output", fullOutputConfig) + cmd := exec.Command("/usr/bin/config-forker", "--job-config", updateJobConfig, "--version", version, "--output", fullOutputConfig) bytes, err := cmd.CombinedOutput() if err != nil { log.Printf("ERROR: config-forker command output: %s : %s ", string(bytes), err) @@ -1271,3 +1277,70 @@ func main() { } } } + +func updatePhase2Jobs(orgRepo, fullJobConfig string) (string, error) { + fileContent, err := ioutil.ReadFile(fullJobConfig) + if err != nil { + return "", fmt.Errorf("Error reading yaml file: %v", err) + } + + var data map[string]interface{} + if err := yaml.Unmarshal(fileContent, &data); err != nil { + return "", fmt.Errorf("Error unmarshalling yaml: %v", err) + } + + err = updatePresubmits(orgRepo, data) + if err != nil { + return "", err + } + + updatedContent, err := yaml.Marshal(data) + if err != nil { + return "", fmt.Errorf("Error marshalling yaml: %v", err) + } + + updateJobConfig := os.TempDir() + "/job-config.yaml" + if err := ioutil.WriteFile(updateJobConfig, updatedContent, 0644); err != nil { + return "", fmt.Errorf("Error writing updated yaml to file: %v", err) + } + + return updateJobConfig, nil +} + +func updatePresubmits(orgRepo string, data map[string]interface{}) error { + presubmits, ok := data["presubmits"].(map[string]interface{}) + if !ok { + return fmt.Errorf("Missing or invalid 'presubmits' section in yaml") + } + + repo, ok := presubmits[orgRepo].([]interface{}) + if !ok { + return fmt.Errorf("Missing or invalid section for repository '%s' in yaml", orgRepo) + } + + for i, job := range repo { + jobMap, ok := job.(map[string]interface{}) + if !ok { + return fmt.Errorf("Invalid job format in yaml") + } + + if shouldUpdateJob(jobMap) { + jobMap["always_run"] = true + presubmits[orgRepo].([]interface{})[i] = jobMap + } + } + + return nil +} + +func shouldUpdateJob(jobMap map[string]interface{}) bool { + optional, optionalOk := jobMap["optional"].(bool) + alwaysRun, alwaysRunOk := jobMap["always_run"].(bool) + runIfChanged, runIfChangedOk := jobMap["run_if_changed"].(string) + skipIfOnlyChanged, skipIfOnlyChangedOk := jobMap["skip_if_only_changed"].(string) + + return (!alwaysRunOk || !alwaysRun) && + (!optionalOk || !optional) && + (!runIfChangedOk || runIfChanged == "") && + (!skipIfOnlyChangedOk || skipIfOnlyChanged == "") +} diff --git a/releng/release-tool/release-tool_test.go b/releng/release-tool/release-tool_test.go index 751ba5e281..32b329c99e 100644 --- a/releng/release-tool/release-tool_test.go +++ b/releng/release-tool/release-tool_test.go @@ -4,10 +4,13 @@ import ( "fmt" "io/ioutil" "os" + "path/filepath" + "reflect" "testing" "time" "github.com/google/go-github/v32/github" + "sigs.k8s.io/yaml" ) func standardCleanup(r *releaseData) { @@ -457,3 +460,93 @@ func TestNewTag(t *testing.T) { } } } + +func TestUpdatePhase2Jobs(t *testing.T) { + var ( + orgRepo = "kubevirt/kubevirt" + jobConfig string + originalConfig map[string]interface{} + ) + + originalConfig = map[string]interface{}{ + "presubmits": map[string]interface{}{ + orgRepo: []interface{}{ + map[string]interface{}{ + "name": "phase1", + "always_run": true, + }, + map[string]interface{}{ + "name": "phase2", + "always_run": false, + }, + map[string]interface{}{ + "name": "optional_job", + "always_run": false, + "optional": true, + }, + }, + }, + } + + jobConfig = filepath.Join(os.TempDir(), "job-config.yaml") + err := writeYAMLToFile(originalConfig, jobConfig) + if err != nil { + t.Errorf("writeYAMLToFile failed %s", err) + } + + defer os.Remove(jobConfig) + + updatedJobConfig, err := updatePhase2Jobs(orgRepo, jobConfig) + if err != nil { + t.Errorf("updatePhase2Jobs failed %s", err) + } + + updatedContent, err := ioutil.ReadFile(updatedJobConfig) + if err != nil { + t.Errorf("updatedContent failed %s", err) + } + + var updatedData map[string]interface{} + err = yaml.Unmarshal(updatedContent, &updatedData) + if err != nil { + t.Errorf("Unmarshal updatedContent failed %s", err) + } + + expectedUpdatedData := map[string]interface{}{ + "presubmits": map[string]interface{}{ + orgRepo: []interface{}{ + map[string]interface{}{ + "name": "phase1", + "always_run": true, + }, + map[string]interface{}{ + "name": "phase2", + "always_run": true, + }, + map[string]interface{}{ + "name": "optional_job", + "always_run": false, + "optional": true, + }, + }, + }, + } + + if !reflect.DeepEqual(expectedUpdatedData, updatedData) { + t.Errorf("updatedData doesn't equal expectedUpdatedData") + } +} + +func writeYAMLToFile(data map[string]interface{}, fileName string) error { + yamlContent, err := yaml.Marshal(data) + if err != nil { + return err + } + + err = ioutil.WriteFile(fileName, yamlContent, 0644) + if err != nil { + return err + } + + return nil +}