Skip to content

Commit

Permalink
Merge pull request #3550 from saschagrunert/schedule-builder-eol
Browse files Browse the repository at this point in the history
Add `-e/--eol-config-path` support to `schedule-builder`
  • Loading branch information
k8s-ci-robot authored Apr 4, 2024
2 parents 7fc79f9 + 4917b8c commit 5095b6e
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 36 deletions.
61 changes: 51 additions & 10 deletions cmd/schedule-builder/cmd/markdown.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,22 +167,34 @@ func processFile(fileName string, vars interface{}) string {
return process(tmpl, vars)
}

func updatePatchSchedule(refTime time.Time, schedule PatchSchedule, filePath string) error {
func updatePatchSchedule(refTime time.Time, schedule PatchSchedule, eolBranches EolBranches, filePath, eolFilePath string) error {
const refDate = "2006-01-02"

for _, schedule := range schedule.Schedules {
removeSchedules := []int{}
for i, sched := range schedule.Schedules {
for {
eolDate, err := time.Parse(refDate, schedule.EndOfLifeDate)
eolDate, err := time.Parse(refDate, sched.EndOfLifeDate)
if err != nil {
return fmt.Errorf("parse end of life date: %w", err)
}

if refTime.After(eolDate) {
logrus.Infof("Skipping end of life release: %s", schedule.Release)
if eolFilePath == "" {
logrus.Infof("Skipping end of life release: %s", sched.Release)
break
}

logrus.Infof("Moving %s to end of life", sched.Release)
eolBranches.Branches = append([]*EolBranch{{
Release: sched.Release,
FinalPatchRelease: sched.Next.Release,
EndOfLifeDate: sched.Next.TargetDate,
}}, eolBranches.Branches...)
removeSchedules = append(removeSchedules, i)
break
}

targetDate, err := time.Parse(refDate, schedule.Next.TargetDate)
targetDate, err := time.Parse(refDate, sched.Next.TargetDate)
if err != nil {
return fmt.Errorf("parse target date: %w", err)
}
Expand All @@ -192,18 +204,18 @@ func updatePatchSchedule(refTime time.Time, schedule PatchSchedule, filePath str
}

// Copy the release to the previousPatches section
schedule.PreviousPatches = append([]*PatchRelease{schedule.Next}, schedule.PreviousPatches...)
sched.PreviousPatches = append([]*PatchRelease{sched.Next}, sched.PreviousPatches...)

// Create a new next release
nextReleaseVersion, err := util.TagStringToSemver(schedule.Next.Release)
nextReleaseVersion, err := util.TagStringToSemver(sched.Next.Release)
if err != nil {
return fmt.Errorf("parse semver version: %w", err)
}
if err := nextReleaseVersion.IncrementPatch(); err != nil {
return fmt.Errorf("increment patch version: %w", err)
}

cherryPickDeadline, err := time.Parse(refDate, schedule.Next.CherryPickDeadline)
cherryPickDeadline, err := time.Parse(refDate, sched.Next.CherryPickDeadline)
if err != nil {
return fmt.Errorf("parse cherry pick deadline: %w", err)
}
Expand All @@ -215,16 +227,31 @@ func updatePatchSchedule(refTime time.Time, schedule PatchSchedule, filePath str
targetDateDay := secondTuesday(targetDatePlusOneMonth)
newTargetDate := time.Date(targetDatePlusOneMonth.Year(), targetDatePlusOneMonth.Month(), targetDateDay, 0, 0, 0, 0, time.UTC)

schedule.Next = &PatchRelease{
sched.Next = &PatchRelease{
Release: nextReleaseVersion.String(),
CherryPickDeadline: newCherryPickDeadline.Format(refDate),
TargetDate: newTargetDate.Format(refDate),
}

logrus.Infof("Adding release schedule: %+v", schedule.Next)
logrus.Infof("Adding release schedule: %+v", sched.Next)
}
}

newSchedules := []*Schedule{}
for i, sched := range schedule.Schedules {
appendItem := true
for _, k := range removeSchedules {
if i == k {
appendItem = false
break
}
}
if appendItem {
newSchedules = append(newSchedules, sched)
}
}
schedule.Schedules = newSchedules

newUpcomingReleases := []*PatchRelease{}
latestDate := refTime
for _, upcomingRelease := range schedule.UpcomingReleases {
Expand Down Expand Up @@ -275,6 +302,20 @@ func updatePatchSchedule(refTime time.Time, schedule PatchSchedule, filePath str
return fmt.Errorf("write schedule YAML: %w", err)
}

if eolFilePath != "" {
logrus.Infof("Writing end of life branches: %s", eolFilePath)

yamlBytes, err := yaml.Marshal(eolBranches)
if err != nil {
return fmt.Errorf("marshal end of life YAML: %w", err)
}

//nolint:gocritic,gosec
if err := os.WriteFile(eolFilePath, yamlBytes, 0o644); err != nil {
return fmt.Errorf("write end of life YAML: %w", err)
}
}

logrus.Infof("Wrote schedule YAML to: %v", filePath)
return nil
}
Expand Down
44 changes: 33 additions & 11 deletions cmd/schedule-builder/cmd/markdown_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ func TestUpdatePatchSchedule(t *testing.T) {
name string
refTime time.Time
givenSchedule, expectedSchedule PatchSchedule
expectedEolBranches EolBranches
}{
{
name: "succeed to update the schedule",
Expand All @@ -368,6 +369,11 @@ func TestUpdatePatchSchedule(t *testing.T) {
{ // EOL
Release: "1.20",
EndOfLifeDate: "2023-01-01",
Next: &PatchRelease{
Release: "1.20.10",
CherryPickDeadline: "2023-12-08",
TargetDate: "2023-12-12",
},
},
},
UpcomingReleases: []*PatchRelease{
Expand Down Expand Up @@ -417,10 +423,6 @@ func TestUpdatePatchSchedule(t *testing.T) {
},
},
},
{
Release: "1.20",
EndOfLifeDate: "2023-01-01",
},
},
UpcomingReleases: []*PatchRelease{
{
Expand All @@ -440,21 +442,41 @@ func TestUpdatePatchSchedule(t *testing.T) {
},
},
},
expectedEolBranches: EolBranches{
Branches: []*EolBranch{
{
Release: "1.20",
FinalPatchRelease: "1.20.10",
EndOfLifeDate: "2023-12-12",
},
},
},
},
} {
t.Run(tc.name, func(t *testing.T) {
file, err := os.CreateTemp("", "schedule-")
scheduleFile, err := os.CreateTemp("", "schedule-")
require.NoError(t, err)
require.NoError(t, scheduleFile.Close())

eolFile, err := os.CreateTemp("", "eol-")
require.NoError(t, err)
require.NoError(t, eolFile.Close())

require.NoError(t, updatePatchSchedule(tc.refTime, tc.givenSchedule, EolBranches{}, scheduleFile.Name(), eolFile.Name()))

scheduleYamlBytes, err := os.ReadFile(scheduleFile.Name())
require.NoError(t, err)
require.NoError(t, file.Close())
patchRes := PatchSchedule{}
require.NoError(t, yaml.UnmarshalStrict(scheduleYamlBytes, &patchRes))

require.NoError(t, updatePatchSchedule(tc.refTime, tc.givenSchedule, file.Name()))
assert.Equal(t, tc.expectedSchedule, patchRes)

yamlBytes, err := os.ReadFile(file.Name())
eolYamlBytes, err := os.ReadFile(eolFile.Name())
require.NoError(t, err)
res := PatchSchedule{}
require.NoError(t, yaml.UnmarshalStrict(yamlBytes, &res))
eolRes := EolBranches{}
require.NoError(t, yaml.UnmarshalStrict(eolYamlBytes, &eolRes))

assert.Equal(t, tc.expectedSchedule, res)
assert.Equal(t, tc.expectedEolBranches, eolRes)
})
}
}
13 changes: 13 additions & 0 deletions cmd/schedule-builder/cmd/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,19 @@ type Schedule struct {
PreviousPatches []*PatchRelease `json:"previousPatches,omitempty" yaml:"previousPatches,omitempty"`
}

// EolBranches is main struct to hold the end of life branches.
type EolBranches struct {
Branches []*EolBranch `json:"branches,omitempty" yaml:"branches,omitempty"`
}

// EolBranch struct to define the end of life release branches.
type EolBranch struct {
Release string `json:"release,omitempty" yaml:"release,omitempty"`
FinalPatchRelease string `json:"finalPatchRelease,omitempty" yaml:"finalPatchRelease,omitempty"`
EndOfLifeDate string `json:"endOfLifeDate,omitempty" yaml:"endOfLifeDate,omitempty"`
Note string `json:"note,omitempty" yaml:"note,omitempty"`
}

type ReleaseSchedule struct {
Releases []Release `yaml:"releases"`
}
Expand Down
58 changes: 43 additions & 15 deletions cmd/schedule-builder/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,24 +43,26 @@ var rootCmd = &cobra.Command{
}

type options struct {
configPath string
outputFile string
logLevel string
typeFile string
update bool
version bool
configPath string
eolConfigPath string
outputFile string
logLevel string
typeFile string
update bool
version bool
}

var opts = &options{}

const (
configPathFlag = "config-path"
outputFileFlag = "output-file"
typeFlag = "type"
updateFlag = "update"
versionFlag = "version"
typePatch = "patch"
typeRelease = "release"
configPathFlag = "config-path"
eolConfigPathFlag = "eol-config-path"
outputFileFlag = "output-file"
typeFlag = "type"
updateFlag = "update"
versionFlag = "version"
typePatch = "patch"
typeRelease = "release"
)

// Execute adds all child commands to the root command and sets flags appropriately.
Expand All @@ -80,6 +82,14 @@ func init() {
"path where can find the schedule.yaml file",
)

rootCmd.PersistentFlags().StringVarP(
&opts.eolConfigPath,
eolConfigPathFlag,
"e",
"",
"path where can find the eol.yaml file for updating end of life releases",
)

rootCmd.PersistentFlags().StringVarP(
&opts.outputFile,
outputFileFlag,
Expand Down Expand Up @@ -145,6 +155,7 @@ func run(opts *options) error {
var (
patchSchedule PatchSchedule
releaseSchedule ReleaseSchedule
eolBranches EolBranches
scheduleOut string
)

Expand All @@ -153,12 +164,29 @@ func run(opts *options) error {
switch opts.typeFile {
case typePatch:
if err := yaml.UnmarshalStrict(data, &patchSchedule); err != nil {
return fmt.Errorf("failed to decode the file: %w", err)
return fmt.Errorf("failed to decode patch schedule: %w", err)
}

if opts.eolConfigPath != "" {
data, err := os.ReadFile(opts.eolConfigPath)
if err != nil {
return fmt.Errorf("failed to read end of life config path: %w", err)
}

if err := yaml.UnmarshalStrict(data, &eolBranches); err != nil {
return fmt.Errorf("failed to decode end of life branches: %w", err)
}
}

if opts.update {
logrus.Info("Updating schedule")
if err := updatePatchSchedule(time.Now(), patchSchedule, opts.configPath); err != nil {
if err := updatePatchSchedule(
time.Now(),
patchSchedule,
eolBranches,
opts.configPath,
opts.eolConfigPath,
); err != nil {
return fmt.Errorf("update patch schedule: %w", err)
}
} else {
Expand Down

0 comments on commit 5095b6e

Please sign in to comment.