From eb1f8f15c331296baea7aa1c9c98c66180b94de5 Mon Sep 17 00:00:00 2001 From: Chai Bot Date: Thu, 20 Aug 2026 23:51:18 +0000 Subject: [PATCH 1/2] Fix development_start parsing for Sippy date-only format Sippy PR openshift/sippy#3716 changed the development_start and ga fields in /api/releases from RFC 3339 timestamps to bare date strings (e.g. "2018-07-11"), which broke getLatestRelease() in the featuregate-test-analyzer with: parsing time "2018-07-11" as "2006-01-02T15:04:05Z07:00": cannot parse "" as "T" Add a flexibleDateTime wrapper whose UnmarshalJSON tries RFC 3339 first and falls back to the date-only layout ("2006-01-02"), so both the old and new /api/releases response formats are handled. Co-Authored-By: Claude Opus 4.8 --- .../codegen/cmd/featuregate-test-analyzer.go | 42 ++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/tools/codegen/cmd/featuregate-test-analyzer.go b/tools/codegen/cmd/featuregate-test-analyzer.go index eb76143a2c6..ae874d54dea 100644 --- a/tools/codegen/cmd/featuregate-test-analyzer.go +++ b/tools/codegen/cmd/featuregate-test-analyzer.go @@ -801,6 +801,38 @@ func filterVariants(featureGate string, variantsList ...[]JobVariant) []JobVaria return filteredVariants } +// flexibleDateTime wraps time.Time so that JSON values can be decoded from +// either an RFC 3339 timestamp (e.g. "2018-07-11T00:00:00Z") or a bare +// date-only string (e.g. "2018-07-11"). The Sippy /api/releases endpoint +// switched the development_start and ga fields from RFC 3339 timestamps to +// bare dates; supporting both keeps this tool compatible with old and new +// responses. +type flexibleDateTime struct { + time.Time +} + +// UnmarshalJSON decodes the JSON value into the wrapped time.Time, trying the +// RFC 3339 layout first and falling back to the date-only layout ("2006-01-02"). +func (f *flexibleDateTime) UnmarshalJSON(data []byte) error { + s := strings.Trim(string(data), `"`) + if s == "" || s == "null" { + return nil + } + + if t, err := time.Parse(time.RFC3339, s); err == nil { + f.Time = t + return nil + } + + t, err := time.Parse("2006-01-02", s) + if err != nil { + return fmt.Errorf("cannot parse %q as RFC 3339 or date-only (2006-01-02): %w", s, err) + } + + f.Time = t + return nil +} + // getLatestRelease returns the latest release from Sippy. func getLatestRelease() (string, error) { releaseAPI := "https://sippy.dptools.openshift.org/api/releases" @@ -821,8 +853,8 @@ func getLatestRelease() (string, error) { var result struct { ReleaseAttrs map[string]struct { - DevelopmentStart *time.Time `json:"development_start,omitempty"` - Product string `json:"product,omitempty"` + DevelopmentStart *flexibleDateTime `json:"development_start,omitempty"` + Product string `json:"product,omitempty"` } `json:"release_attrs,omitempty"` } @@ -840,14 +872,14 @@ func getLatestRelease() (string, error) { continue } - if releaseAttrs.DevelopmentStart != nil && !releaseAttrs.DevelopmentStart.IsZero() && time.Now().Before(*releaseAttrs.DevelopmentStart) { + if releaseAttrs.DevelopmentStart != nil && !releaseAttrs.DevelopmentStart.Time.IsZero() && time.Now().Before(releaseAttrs.DevelopmentStart.Time) { // We only want to consider releases that have started development. continue } - if releaseAttrs.DevelopmentStart != nil && !releaseAttrs.DevelopmentStart.IsZero() && releaseAttrs.DevelopmentStart.After(latestReleaseStart) { + if releaseAttrs.DevelopmentStart != nil && !releaseAttrs.DevelopmentStart.Time.IsZero() && releaseAttrs.DevelopmentStart.Time.After(latestReleaseStart) { latestRelease = release - latestReleaseStart = *releaseAttrs.DevelopmentStart + latestReleaseStart = releaseAttrs.DevelopmentStart.Time } } From 6424d8b88214c46d4ddc805f558402da7839a2ae Mon Sep 17 00:00:00 2001 From: Chai Bot Date: Fri, 21 Aug 2026 00:18:48 +0000 Subject: [PATCH 2/2] Fix Sippy job runs timestamp filter for timestamptz column Sippy PR openshift/sippy#3716 changed the timestamp column backing /api/jobs/runs to a raw timestamptz Postgres type. BuildSippyJobRunsForJobURL() was sending the >= filter value as epoch milliseconds (fmt.Sprintf("%d", timestamp.UnixMilli())), which Postgres cannot parse as a timestamp, so the /api/jobs/runs call failed. Send the filter value as an RFC 3339 string (timestamp.UTC().Format(time.RFC3339), e.g. "2026-08-07T00:00:00Z") which Postgres parses natively as timestamptz. Co-Authored-By: Claude Opus 4.8 --- tools/codegen/pkg/sippy/json_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/codegen/pkg/sippy/json_types.go b/tools/codegen/pkg/sippy/json_types.go index ad2012a3ef5..ece3044dea4 100644 --- a/tools/codegen/pkg/sippy/json_types.go +++ b/tools/codegen/pkg/sippy/json_types.go @@ -337,7 +337,7 @@ func BuildSippyJobRunsForJobURL(release, jobName string, timestamp time.Time) st { ColumnField: "timestamp", OperatorValue: ">=", - Value: fmt.Sprintf("%d", timestamp.UnixMilli()), + Value: timestamp.UTC().Format(time.RFC3339), }, }