diff --git a/cmd/schedule-builder/cmd/markdown.go b/cmd/schedule-builder/cmd/markdown.go index 115b96a3a12..d50ff8c1b85 100644 --- a/cmd/schedule-builder/cmd/markdown.go +++ b/cmd/schedule-builder/cmd/markdown.go @@ -37,7 +37,25 @@ var tpls embed.FS // runs with `--type=patch` to return the patch schedule func parsePatchSchedule(patchSchedule PatchSchedule) string { output := []string{} + + if len(patchSchedule.UpcomingReleases) > 0 { + output = append(output, "### Upcoming Monthly Releases\n") + tableString := &strings.Builder{} + table := tablewriter.NewWriter(tableString) + table.SetAutoWrapText(false) + table.SetHeader([]string{"Monthly Patch Release", "Cherry Pick Deadline", "Target Date"}) + for _, upcoming := range patchSchedule.UpcomingReleases { + table.Append([]string{strings.TrimSpace(upcoming.Release), strings.TrimSpace(upcoming.CherryPickDeadline), strings.TrimSpace(upcoming.TargetDate)}) + } + table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false}) + table.SetCenterSeparator("|") + table.Render() + + output = append(output, tableString.String()) + } + output = append(output, "### Timeline\n") + for _, releaseSchedule := range patchSchedule.Schedules { output = append(output, fmt.Sprintf("### %s\n", releaseSchedule.Release), fmt.Sprintf("Next patch release is **%s**\n", releaseSchedule.Next.Release), @@ -207,6 +225,46 @@ func updatePatchSchedule(refTime time.Time, schedule PatchSchedule, filePath str } } + newUpcomingReleases := []*PatchRelease{} + latestDate := refTime + for _, upcomingRelease := range schedule.UpcomingReleases { + upcomingTargetDate, err := time.Parse(refDate, upcomingRelease.TargetDate) + if err != nil { + return fmt.Errorf("parse upcoming release target date: %w", err) + } + + if refTime.After(upcomingTargetDate) { + logrus.Infof("Skipping outdated upcoming release for %s (%s)", upcomingRelease.Release, upcomingRelease.TargetDate) + continue + } + + logrus.Infof("Using existing upcoming release for %s (%s)", upcomingRelease.Release, upcomingRelease.TargetDate) + newUpcomingReleases = append(newUpcomingReleases, upcomingRelease) + latestDate = upcomingTargetDate + } + for { + if len(newUpcomingReleases) >= 3 { + logrus.Infof("Got 3 new upcoming releases, not adding any more") + break + } + + latestDate = latestDate.AddDate(0, 1, 0) + cherryPickDay := firstFriday(latestDate) + targetDateDay := secondTuesday(latestDate) + nextCherryPickDeadline := time.Date(latestDate.Year(), latestDate.Month(), cherryPickDay, 0, 0, 0, 0, time.UTC) + nextTargetDate := time.Date(latestDate.Year(), latestDate.Month(), targetDateDay, 0, 0, 0, 0, time.UTC) + + releaseName := nextTargetDate.Format("January 2006") + logrus.Infof("Adding new upcoming release for %s", releaseName) + + newUpcomingReleases = append(newUpcomingReleases, &PatchRelease{ + Release: releaseName, + CherryPickDeadline: nextCherryPickDeadline.Format(refDate), + TargetDate: nextTargetDate.Format(refDate), + }) + } + schedule.UpcomingReleases = newUpcomingReleases + yamlBytes, err := yaml.Marshal(schedule) if err != nil { return fmt.Errorf("marshal schedule YAML: %w", err) diff --git a/cmd/schedule-builder/cmd/markdown_test.go b/cmd/schedule-builder/cmd/markdown_test.go index 4de092253d7..bf30df6fd4b 100644 --- a/cmd/schedule-builder/cmd/markdown_test.go +++ b/cmd/schedule-builder/cmd/markdown_test.go @@ -27,7 +27,13 @@ import ( "sigs.k8s.io/yaml" ) -const expectedPatchSchedule = `### Timeline +const expectedPatchSchedule = `### Upcoming Monthly Releases + +| MONTHLY PATCH RELEASE | CHERRY PICK DEADLINE | TARGET DATE | +|-----------------------|----------------------|-------------| +| June 2020 | 2020-06-12 | 2020-06-17 | + +### Timeline ### X.Y @@ -158,6 +164,13 @@ func TestParsePatchSchedule(t *testing.T) { }, }, }, + UpcomingReleases: []*PatchRelease{ + { + Release: "June 2020", + CherryPickDeadline: "2020-06-12", + TargetDate: "2020-06-17", + }, + }, }, }, { @@ -193,6 +206,13 @@ func TestParsePatchSchedule(t *testing.T) { }, }, }, + UpcomingReleases: []*PatchRelease{ + { + Release: "June 2020", + CherryPickDeadline: "2020-06-12", + TargetDate: "2020-06-17", + }, + }, }, }, } @@ -350,6 +370,23 @@ func TestUpdatePatchSchedule(t *testing.T) { EndOfLifeDate: "2023-01-01", }, }, + UpcomingReleases: []*PatchRelease{ + { + Release: "March 2024", + CherryPickDeadline: "2024-03-08", + TargetDate: "2024-03-13", + }, + { + Release: "April 2024", + CherryPickDeadline: "2024-04-12", + TargetDate: "2024-04-17", + }, + { + Release: "May 2024", + CherryPickDeadline: "2024-05-10", + TargetDate: "2024-05-14", + }, + }, }, expectedSchedule: PatchSchedule{ Schedules: []*Schedule{ @@ -385,6 +422,23 @@ func TestUpdatePatchSchedule(t *testing.T) { EndOfLifeDate: "2023-01-01", }, }, + UpcomingReleases: []*PatchRelease{ + { + Release: "April 2024", + CherryPickDeadline: "2024-04-12", + TargetDate: "2024-04-17", + }, + { + Release: "May 2024", + CherryPickDeadline: "2024-05-10", + TargetDate: "2024-05-14", + }, + { + Release: "June 2024", + CherryPickDeadline: "2024-06-07", + TargetDate: "2024-06-11", + }, + }, }, }, } { diff --git a/cmd/schedule-builder/cmd/model.go b/cmd/schedule-builder/cmd/model.go index b0f2caf2c2e..f0bd12ef309 100644 --- a/cmd/schedule-builder/cmd/model.go +++ b/cmd/schedule-builder/cmd/model.go @@ -18,7 +18,8 @@ package cmd // PatchSchedule main struct to hold the schedules. type PatchSchedule struct { - Schedules []*Schedule `json:"schedules,omitempty" yaml:"schedules,omitempty"` + UpcomingReleases []*PatchRelease `json:"upcoming_releases,omitempty" yaml:"upcoming_releases,omitempty"` + Schedules []*Schedule `json:"schedules,omitempty" yaml:"schedules,omitempty"` } // PatchRelease struct to define the patch schedules.