diff --git a/RELEASE b/RELEASE index 419bdfe053..34ea9212bb 100644 --- a/RELEASE +++ b/RELEASE @@ -1,6 +1,6 @@ # Generated by `make release` command. # DO NOT EDIT. -tag: v0.51.2 +tag: v0.51.3 releaseNoteGenerator: showCommitter: false diff --git a/pkg/app/piped/driftdetector/cloudrun/detector.go b/pkg/app/piped/driftdetector/cloudrun/detector.go index fc0386c995..5f31f520dc 100644 --- a/pkg/app/piped/driftdetector/cloudrun/detector.go +++ b/pkg/app/piped/driftdetector/cloudrun/detector.go @@ -211,6 +211,7 @@ func (d *detector) checkApplication(ctx context.Context, app *model.Application, diff.WithEquateEmpty(), diff.WithIgnoreAddingMapKeys(), diff.WithCompareNumberAndNumericString(), + diff.WithCompareBooleanAndBooleanString(), ) if err != nil { return err diff --git a/pkg/app/piped/planpreview/cloudrundiff.go b/pkg/app/piped/planpreview/cloudrundiff.go index 1ffa3a4794..8ad7212996 100644 --- a/pkg/app/piped/planpreview/cloudrundiff.go +++ b/pkg/app/piped/planpreview/cloudrundiff.go @@ -66,6 +66,7 @@ func (b *builder) cloudrundiff( newManifest, diff.WithEquateEmpty(), diff.WithCompareNumberAndNumericString(), + diff.WithCompareBooleanAndBooleanString(), ) if err != nil { fmt.Fprintf(buf, "failed to compare manifests (%v)\n", err) diff --git a/pkg/diff/diff.go b/pkg/diff/diff.go index 1d4d1aad14..0542ef655d 100644 --- a/pkg/diff/diff.go +++ b/pkg/diff/diff.go @@ -25,11 +25,12 @@ import ( ) type differ struct { - ignoreAddingMapKeys bool - equateEmpty bool - compareNumberAndNumericString bool - ignoredPaths map[string]struct{} - ignoreConfig map[string][]string + ignoreAddingMapKeys bool + equateEmpty bool + compareNumberAndNumericString bool + compareBooleanAndBooleanString bool + ignoredPaths map[string]struct{} + ignoreConfig map[string][]string result *Result } @@ -60,6 +61,15 @@ func WithCompareNumberAndNumericString() Option { } } +// WithCompareBooleanAndBooleanString configures differ to compare a boolean with a string. +// Differ parses the string to boolean before comparing their values. +// e.g. true == "true" +func WithCompareBooleanAndBooleanString() Option { + return func(d *differ) { + d.compareBooleanAndBooleanString = true + } +} + // WithIgnoreConfig configures ignored fields. func WithIgnoreConfig(config map[string][]string) Option { return func(d *differ) { @@ -170,6 +180,24 @@ func (d *differ) diff(path []PathStep, vx, vy reflect.Value) error { } } + if isBooleanValue(vx) && isBooleanValue(vy) { + return d.diffBool(path, vx, vy) + } + + if d.compareBooleanAndBooleanString { + if isBooleanValue(vx) { + if y, ok := convertToBoolean(vy); ok { + return d.diffBool(path, vx, y) + } + } + + if isBooleanValue(vy) { + if x, ok := convertToBoolean(vx); ok { + return d.diffBool(path, x, vy) + } + } + } + if vx.Type() != vy.Type() { d.addNode(path, vx.Type(), vy.Type(), vx, vy) return nil @@ -360,6 +388,18 @@ func convertToNumber(v reflect.Value) (reflect.Value, bool) { } } +func isBooleanValue(v reflect.Value) bool { + return v.Kind() == reflect.Bool +} + +func convertToBoolean(v reflect.Value) (reflect.Value, bool) { + if v.Kind() == reflect.String { + b, err := strconv.ParseBool(v.String()) + return reflect.ValueOf(b), err == nil + } + return v, false +} + func newSlicePath(path []PathStep, index int) []PathStep { next := make([]PathStep, len(path)) copy(next, path) diff --git a/pkg/diff/diff_test.go b/pkg/diff/diff_test.go index 1f19234e0d..d836905295 100644 --- a/pkg/diff/diff_test.go +++ b/pkg/diff/diff_test.go @@ -42,6 +42,7 @@ func TestDiff(t *testing.T) { WithEquateEmpty(), WithIgnoreAddingMapKeys(), WithCompareNumberAndNumericString(), + WithCompareBooleanAndBooleanString(), }, diffNum: 0, }, diff --git a/pkg/diff/renderer.go b/pkg/diff/renderer.go index 0ad9e5cd7b..b9c077d48f 100644 --- a/pkg/diff/renderer.go +++ b/pkg/diff/renderer.go @@ -206,6 +206,9 @@ func renderNodeValue(v reflect.Value, prefix string) (string, bool) { case reflect.Float32, reflect.Float64: return strconv.FormatFloat(v.Float(), 'f', -1, 64), false + case reflect.Bool: + return strconv.FormatBool(v.Bool()), false + default: return v.String(), false } diff --git a/pkg/diff/renderer_test.go b/pkg/diff/renderer_test.go index d9b0595f92..400c896297 100644 --- a/pkg/diff/renderer_test.go +++ b/pkg/diff/renderer_test.go @@ -43,6 +43,10 @@ func TestRenderNodeValue(t *testing.T) { "one": []string{"one-1", "one-2"}, "two": []string{"two-1", "two-2"}, } + mapOfBool = map[string]interface{}{ + "false": false, + "true": true, + } ) testcases := []struct { @@ -65,6 +69,16 @@ func TestRenderNodeValue(t *testing.T) { value: reflect.ValueOf("hello"), expected: "hello", }, + { + name: "bool value (true)", + value: reflect.ValueOf(true), + expected: "true", + }, + { + name: "bool value (false)", + value: reflect.ValueOf(false), + expected: "false", + }, { name: "slice of primitive elements", value: func() reflect.Value { @@ -142,6 +156,12 @@ two: two-value`, - b 7-string: hi`, }, + { + name: "map of bool", + value: reflect.ValueOf(mapOfBool), + expected: `false: false +true: true`, + }, } for _, tc := range testcases { diff --git a/pkg/diff/testdata/no_diff.yaml b/pkg/diff/testdata/no_diff.yaml index 3f5f689c5e..7a2637023b 100644 --- a/pkg/diff/testdata/no_diff.yaml +++ b/pkg/diff/testdata/no_diff.yaml @@ -9,6 +9,10 @@ metadata: zeroString1: "" zeroInt1: 0 zeroFloat1: 0.0 + booleanString1: "true" + booleanString2: true + booleanString3: "false" + booleanString4: false spec: replicas: 2 number: 1 @@ -53,6 +57,10 @@ metadata: zeroString2: "" zeroInt2: 0 zeroFloat2: 0.0 + booleanString1: true + booleanString2: "true" + booleanString3: false + booleanString4: "false" spec: replicas: 2 number: 1.0 diff --git a/pkg/plugin/diff/diff.go b/pkg/plugin/diff/diff.go index 9b188bd1c1..1f373e0620 100644 --- a/pkg/plugin/diff/diff.go +++ b/pkg/plugin/diff/diff.go @@ -24,11 +24,12 @@ import ( ) type differ struct { - ignoreAddingMapKeys bool - equateEmpty bool - compareNumberAndNumericString bool - ignoredPaths map[string]struct{} - ignoreConfig map[string][]string + ignoreAddingMapKeys bool + equateEmpty bool + compareNumberAndNumericString bool + compareBooleanAndBooleanString bool + ignoredPaths map[string]struct{} + ignoreConfig map[string][]string result *Result } @@ -59,6 +60,15 @@ func WithCompareNumberAndNumericString() Option { } } +// WithCompareBooleanAndBooleanString configures differ to compare a boolean with a string. +// Differ parses the string to boolean before comparing their values. +// e.g. true == "true" +func WithCompareBooleanAndBooleanString() Option { + return func(d *differ) { + d.compareBooleanAndBooleanString = true + } +} + // WithIgnoreConfig configures ignored fields. func WithIgnoreConfig(config map[string][]string) Option { return func(d *differ) { @@ -135,6 +145,24 @@ func (d *differ) diff(path []PathStep, vx, vy reflect.Value) error { } } + if isBooleanValue(vx) && isBooleanValue(vy) { + return d.diffBool(path, vx, vy) + } + + if d.compareBooleanAndBooleanString { + if isBooleanValue(vx) { + if y, ok := convertToBoolean(vy); ok { + return d.diffBool(path, vx, y) + } + } + + if isBooleanValue(vy) { + if x, ok := convertToBoolean(vx); ok { + return d.diffBool(path, x, vy) + } + } + } + if vx.Type() != vy.Type() { d.addNode(path, vx.Type(), vy.Type(), vx, vy) return nil @@ -325,6 +353,18 @@ func convertToNumber(v reflect.Value) (reflect.Value, bool) { } } +func isBooleanValue(v reflect.Value) bool { + return v.Kind() == reflect.Bool +} + +func convertToBoolean(v reflect.Value) (reflect.Value, bool) { + if v.Kind() == reflect.String { + b, err := strconv.ParseBool(v.String()) + return reflect.ValueOf(b), err == nil + } + return v, false +} + func newSlicePath(path []PathStep, index int) []PathStep { next := make([]PathStep, len(path)) copy(next, path) diff --git a/pkg/plugin/diff/diff_test.go b/pkg/plugin/diff/diff_test.go index d1bffcf4c5..f26816b9a0 100644 --- a/pkg/plugin/diff/diff_test.go +++ b/pkg/plugin/diff/diff_test.go @@ -42,6 +42,7 @@ func TestDiff(t *testing.T) { WithEquateEmpty(), WithIgnoreAddingMapKeys(), WithCompareNumberAndNumericString(), + WithCompareBooleanAndBooleanString(), }, diffNum: 0, }, diff --git a/pkg/plugin/diff/renderer.go b/pkg/plugin/diff/renderer.go index 0bdbe22828..a5655aacea 100644 --- a/pkg/plugin/diff/renderer.go +++ b/pkg/plugin/diff/renderer.go @@ -201,6 +201,9 @@ func renderNodeValue(v reflect.Value, prefix string) (string, bool) { case reflect.Float32, reflect.Float64: return strconv.FormatFloat(v.Float(), 'f', -1, 64), false + case reflect.Bool: + return strconv.FormatBool(v.Bool()), false + default: return v.String(), false } diff --git a/pkg/plugin/diff/renderer_test.go b/pkg/plugin/diff/renderer_test.go index 74f621e02f..859d125653 100644 --- a/pkg/plugin/diff/renderer_test.go +++ b/pkg/plugin/diff/renderer_test.go @@ -43,6 +43,10 @@ func TestRenderNodeValue(t *testing.T) { "one": []string{"one-1", "one-2"}, "two": []string{"two-1", "two-2"}, } + mapOfBool = map[string]interface{}{ + "false": false, + "true": true, + } ) testcases := []struct { @@ -65,6 +69,16 @@ func TestRenderNodeValue(t *testing.T) { value: reflect.ValueOf("hello"), expected: "hello", }, + { + name: "bool value (true)", + value: reflect.ValueOf(true), + expected: "true", + }, + { + name: "bool value (false)", + value: reflect.ValueOf(false), + expected: "false", + }, { name: "slice of primitive elements", value: func() reflect.Value { @@ -142,6 +156,12 @@ two: two-value`, - b 7-string: hi`, }, + { + name: "map of bool", + value: reflect.ValueOf(mapOfBool), + expected: `false: false +true: true`, + }, } for _, tc := range testcases { diff --git a/pkg/plugin/diff/testdata/no_diff.yaml b/pkg/plugin/diff/testdata/no_diff.yaml index 3f5f689c5e..6e2fd93610 100644 --- a/pkg/plugin/diff/testdata/no_diff.yaml +++ b/pkg/plugin/diff/testdata/no_diff.yaml @@ -9,6 +9,10 @@ metadata: zeroString1: "" zeroInt1: 0 zeroFloat1: 0.0 + booleanString1: "true" + booleanString2: true + booleanString3: "false" + booleanString4: false spec: replicas: 2 number: 1 @@ -53,6 +57,10 @@ metadata: zeroString2: "" zeroInt2: 0 zeroFloat2: 0.0 + booleanString1: true + booleanString2: "true" + booleanString3: false + booleanString4: "false" spec: replicas: 2 number: 1.0 diff --git a/web/src/components/deployment-trace-page/useGroupedDeploymentTrace.tsx b/web/src/components/deployment-trace-page/useGroupedDeploymentTrace.tsx index 2f2505e74b..9a327bde8d 100644 --- a/web/src/components/deployment-trace-page/useGroupedDeploymentTrace.tsx +++ b/web/src/components/deployment-trace-page/useGroupedDeploymentTrace.tsx @@ -41,7 +41,10 @@ const useGroupedDeploymentTrace = (): GroupedDeploymentTrace => { }, [traceList]); const dates = useMemo( - () => Object.keys(deploymentTracesMap).sort(sortDateFunc), + () => + Object.keys(deploymentTracesMap).sort((a, b) => + sortDateFunc(a, b, "DESC") + ), [deploymentTracesMap] ); diff --git a/web/src/modules/deploymentTrace/index.ts b/web/src/modules/deploymentTrace/index.ts index fce65b8130..9dcba2954f 100644 --- a/web/src/modules/deploymentTrace/index.ts +++ b/web/src/modules/deploymentTrace/index.ts @@ -82,13 +82,13 @@ export const fetchMoreDeploymentTraces = createAsyncThunk< DeploymentTraceFilterOptions, { state: AppState } >("deploymentTrace/fetchMoreList", async (options, thunkAPI) => { - const { deployments } = thunkAPI.getState(); + const { deploymentTrace } = thunkAPI.getState(); const response = await deploymentTracesApi.getDeploymentTraces({ options: convertFilterOptions(options), pageSize: FETCH_MORE_ITEMS_PER_PAGE, - cursor: deployments.cursor, - pageMinUpdatedAt: deployments.minUpdatedAt, + cursor: deploymentTrace.cursor, + pageMinUpdatedAt: deploymentTrace.minUpdatedAt, }); return response;