From ab8c0cc990d2824a4c97863d3d93b5eba978ee8d Mon Sep 17 00:00:00 2001 From: Antoine THEBAUD Date: Fri, 19 Dec 2025 11:30:50 +0100 Subject: [PATCH 1/3] [IGNORE] Move cue/common to shared repo Signed-off-by: Antoine THEBAUD --- .github/CODEOWNERS | 2 + cue-test/common/format.cue | 19 ++++ cue-test/common/transform.cue | 28 ++++++ cue-test/common/url.cue | 17 ++++ cue/common/calculation.cue | 20 +++++ cue/common/datasource.cue | 21 +++++ cue/common/format.cue | 79 ++++++++++++++++ cue/common/legend.cue | 25 ++++++ cue/common/mappings.cue | 58 ++++++++++++ cue/common/metricLabel.cue | 16 ++++ cue/common/migrate/mapping.cue | 123 +++++++++++++++++++++++++ cue/common/proxy/http.cue | 40 +++++++++ cue/common/proxy/sql.cue | 63 +++++++++++++ cue/common/threshold.cue | 26 ++++++ cue/common/transform.cue | 52 +++++++++++ cue/common/url.cue | 16 ++++ cue/common/variable.cue | 16 ++++ cue/cue.mod/module.cue | 7 ++ scripts/validate-cue/validate-cue.go | 129 +++++++++++++++++++++++++++ 19 files changed, 757 insertions(+) create mode 100644 cue-test/common/format.cue create mode 100644 cue-test/common/transform.cue create mode 100644 cue-test/common/url.cue create mode 100644 cue/common/calculation.cue create mode 100644 cue/common/datasource.cue create mode 100644 cue/common/format.cue create mode 100644 cue/common/legend.cue create mode 100644 cue/common/mappings.cue create mode 100644 cue/common/metricLabel.cue create mode 100644 cue/common/migrate/mapping.cue create mode 100644 cue/common/proxy/http.cue create mode 100644 cue/common/proxy/sql.cue create mode 100644 cue/common/threshold.cue create mode 100644 cue/common/transform.cue create mode 100644 cue/common/url.cue create mode 100644 cue/common/variable.cue create mode 100644 cue/cue.mod/module.cue create mode 100644 scripts/validate-cue/validate-cue.go diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index af5c8cb..bfb640f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,6 @@ /components @perses/frontend-maintainers +/cue @AntoineThebaud +/cue-test @AntoineThebaud /dashboards @perses/frontend-maintainers /explore @perses/frontend-maintainers /plugin-system @perses/frontend-maintainers diff --git a/cue-test/common/format.cue b/cue-test/common/format.cue new file mode 100644 index 0000000..2bde374 --- /dev/null +++ b/cue-test/common/format.cue @@ -0,0 +1,19 @@ +// Copyright 2025 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package common + +myFormat: #format & { + decimalPlaces: 0 + shortValues: false +} \ No newline at end of file diff --git a/cue-test/common/transform.cue b/cue-test/common/transform.cue new file mode 100644 index 0000000..28e7c7a --- /dev/null +++ b/cue-test/common/transform.cue @@ -0,0 +1,28 @@ +// Copyright 2025 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package common + +myJoinByColumnValueTransform: #joinByColumnValueTransform & { spec: columns: ["job", "instance"] } + +myMergeColumnsTransform: #mergeColumnsTransform & { + spec: { + columns: ["job", "instance"] + name: "job-instance" + disabled: true + } +} + +myMergeIndexedColumnsTransform: #mergeIndexedColumnsTransform & { spec: column: "instance" } + +mergeSeries: #mergeSeries & { spec: disabled: false } \ No newline at end of file diff --git a/cue-test/common/url.cue b/cue-test/common/url.cue new file mode 100644 index 0000000..a63c9d1 --- /dev/null +++ b/cue-test/common/url.cue @@ -0,0 +1,17 @@ +// Copyright 2025 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package common + +test1: #url & "http://localhost:9090" +test2: #url & "https://localhost:9090" diff --git a/cue/common/calculation.cue b/cue/common/calculation.cue new file mode 100644 index 0000000..7cd6d40 --- /dev/null +++ b/cue/common/calculation.cue @@ -0,0 +1,20 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package common + +#calculation: #calculationAggregation // kind-of default value defined for retro-compatibility. Please rather use below types directly for new plugins. + +#calculationAggregation: "first" | *"last" | "first-number" | "last-number" | "mean" | "sum" | "min" | "max" + +#calculationComparison: "abs" | "relative" diff --git a/cue/common/datasource.cue b/cue/common/datasource.cue new file mode 100644 index 0000000..442fc28 --- /dev/null +++ b/cue/common/datasource.cue @@ -0,0 +1,21 @@ +// Copyright 2025 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package common + +#datasourceSelector: { + datasource?: =~#variableSyntaxRegex | { + kind: string + name?: string + } +} diff --git a/cue/common/format.cue b/cue/common/format.cue new file mode 100644 index 0000000..f9bec81 --- /dev/null +++ b/cue/common/format.cue @@ -0,0 +1,79 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package common + +#format: #simpleFormat | #floatFormat | #shortenableFormat + +#simpleFormat: { + unit?: #dateFormat.unit +} + +#floatFormat: { + unit?: #timeFormat.unit | #percentFormat.unit | #currencyFormat.unit | #temperatureFormat.unit + decimalPlaces?: number +} + +#shortenableFormat: { + unit?: #decimalFormat.unit | #bitsFormat.unit | #bytesFormat.unit | #throughputFormat.unit + decimalPlaces?: number + shortValues?: bool +} + +#timeFormat: { + unit: "nanoseconds" | "microseconds" | "milliseconds" | "seconds" | "minutes" | "hours" | "days" | "weeks" | "months" | "years" + decimalPlaces?: number +} + +#percentFormat: { + unit: "percent" | "percent-decimal" + decimalPlaces?: number +} + +#decimalFormat: { + unit: "decimal" + decimalPlaces?: number + shortValues?: bool +} + +#bitsFormat: { + unit: "bits" | "decbits" + decimalPlaces?: number + shortValues?: bool +} + +#bytesFormat: { + unit: "bytes" | "decbytes" + decimalPlaces?: number + shortValues?: bool +} + +#throughputFormat: { + unit: "bits/sec" | "decbits/sec" | "bytes/sec" | "decbytes/sec" | "counts/sec" | "events/sec" | "messages/sec" | "ops/sec" | "packets/sec" | "reads/sec" | "records/sec" | "requests/sec" | "rows/sec" | "writes/sec" + decimalPlaces?: number + shortValues?: bool +} + +#currencyFormat: { + unit: "aud" | "cad" | "chf" | "cny" | "eur" | "gbp" | "hkd" | "inr" | "jpy" | "krw" | "nok" | "nzd" | "sek" | "sgd" | "usd" + decimalPlaces?: number +} + +#temperatureFormat: { + unit: "celsius" | "fahrenheit" + decimalPlaces?: number +} + +#dateFormat: { + unit: "datetime-iso" | "datetime-us" | "datetime-local" | "date-iso" | "date-us" | "date-local" | "time-local" | "time-iso" | "time-us" | "relative-time" | "unix-timestamp" | "unix-timestamp-ms" +} \ No newline at end of file diff --git a/cue/common/legend.cue b/cue/common/legend.cue new file mode 100644 index 0000000..7f30a18 --- /dev/null +++ b/cue/common/legend.cue @@ -0,0 +1,25 @@ +// Copyright 2025 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package common + +#legend: { + position: "bottom" | "right" + mode?: "list" | "table" + size?: "small" | "medium" +} + +#legendWithValues: { + #legend + values?: [...#calculation] +} diff --git a/cue/common/mappings.cue b/cue/common/mappings.cue new file mode 100644 index 0000000..1484019 --- /dev/null +++ b/cue/common/mappings.cue @@ -0,0 +1,58 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package common + +import ( + "strings" +) + +#valueCondition: { + kind: "Value" + spec: { + value: strings.MinRunes(1) + result: #mappingResult + } +} + +#rangeCondition: { + kind: "Range" + spec: { + from?: number + to?: number + result: #mappingResult + } +} + +#regexCondition: { + kind: "Regex" + spec: { + pattern: strings.MinRunes(1) + result: #mappingResult + } +} + +#miscCondition: { + kind: "Misc" + spec: { + value: "empty" | "null" | "NaN" | "true" | "false" + result: #mappingResult + } +} + +#mappingResult: { + value: string + color?: =~"^#(?:[0-9a-fA-F]{3}){1,2}$" +} + +#mappings: #valueCondition | #rangeCondition | #regexCondition | #miscCondition diff --git a/cue/common/metricLabel.cue b/cue/common/metricLabel.cue new file mode 100644 index 0000000..04f6c41 --- /dev/null +++ b/cue/common/metricLabel.cue @@ -0,0 +1,16 @@ +// Copyright 2025 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package common + +#metricLabel: string diff --git a/cue/common/migrate/mapping.cue b/cue/common/migrate/mapping.cue new file mode 100644 index 0000000..fe945db --- /dev/null +++ b/cue/common/migrate/mapping.cue @@ -0,0 +1,123 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package migrate + +#mapping: { + // mapping table for the unit attribute (key = grafana unit, value = perses equivalent) + unit: { + // time units + ms: "milliseconds" + s: "seconds" + m: "minutes" + h: "hours" + d: "days" + dtdurations: "seconds" + dtdurationms: "milliseconds" + // percent units + percent: "percent" + percentunit: "percent-decimal" + // decimal units + none: "decimal" + short: "decimal" + // TODO + // bits units + bits: "bits" + decbits: "decbits" + // bytes units + bytes: "bytes" + decbytes: "decbytes" + // throughput units + bps: "bits/sec" + binbps: "decbits/sec" + Bps: "bytes/sec" + binBps: "decbytes/sec" + cps: "counts/sec" + mps: "messages/sec" + recps: "records/sec" + reqps: "requests/sec" + rowsps: "rows/sec" + rps: "reads/sec" + ops: "ops/sec" + pps: "packets/sec" + wps: "writes/sec" + } + // mapping table for the calculation attribute (key = grafana unit, value = perses equivalent) + calc: { + // Values with a single potential mapping. + first: "first" + firstNotNull: "first-number" + lastNotNull: "last-number" + min: "min" + max: "max" + + // Both keys can be used for "Last" depending on the version of grafana + // and how the calculation is being used (e.g. chart vs table legend + // value). + current: "last" + last: "last" + + // Both keys can be used for "Mean" depending on the version of grafana + // and how the calculation is being used (e.g. chart vs table legend + // value). + avg: "mean" + mean: "mean" + + // Both keys can be used for "Mean" depending on the version of grafana + // and how the calculation is being used (e.g. chart vs table legend + // value). + total: "sum" + sum: "sum" + } + color: { + // mapping array for some color attributes (index = color name in Grafana, value = hexadecimal equivalent that Perses can deal with) + "dark-red": "#c4162a" + "semi-dark-red": "#e02f44" + "red": "#f2495c" + "light-red": "#ff7383" + "super-light-red": "#ffa6b0" + + "dark-orange": "#fa6400" + "semi-dark-orange": "#ff780a" + "orange": "#ff9830" + "light-orange": "#ffb357" + "super-light-orange": "#ffcb7d" + + "dark-green": "#37872d" + "semi-dark-green": "#56a64b" + "green": "#73bf69" + "light-green": "#96d98d" + "super-light-green": "#c8f2c2" + + "dark-blue": "#1f60c4" + "semi-dark-blue": "#3274d9" + "blue": "#5794f2" + "light-blue": "#8ab8ff" + "super-light-blue": "#c0d8ff" + + "dark-purple": "#8f3bb8" + "semi-dark-purple": "#a352cc" + "purple": "#b877d9" + "light-purple": "#b877d9" + "super-light-purple": "#deb6f2" + + "dark-yellow": "#e0b400" + "semi-dark-yellow": "#f2cc0c" + "yellow": "#fade2a" + "light-yellow": "#ffee52" + "super-light-yellow": "#fff899" + + "transparent": "#00000000" + } +} +#defaultCalc: "last" diff --git a/cue/common/proxy/http.cue b/cue/common/proxy/http.cue new file mode 100644 index 0000000..e23179e --- /dev/null +++ b/cue/common/proxy/http.cue @@ -0,0 +1,40 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package proxy + +import ( + "github.com/perses/perses/cue/common" +) + +#HTTPAllowedEndpoint: { + endpointPattern: string + method: "POST" | "PUT" | "PATCH" | "GET" | "DELETE" +} + +#HTTPProxy: { + kind: "HTTPProxy" + spec: { + // url is the url of the datasource. It is not the url of the proxy. + // The Perses server is the proxy, so it needs to know where to redirect the request. + url: common.#url + // allowedEndpoints is a list of tuples of http methods and http endpoints that will be accessible. + // Leave it empty if you don't want to restrict the access to the datasource. + allowedEndpoints?: [...#HTTPAllowedEndpoint] + // headers can be used to provide additional headers that need to be forwarded when requesting the datasource + headers?: {[string]: string} + // secret is the name of the secret that should be used for the proxy or discovery configuration + // It will contain any sensitive information such as password, token, certificate. + secret?: string + } +} diff --git a/cue/common/proxy/sql.cue b/cue/common/proxy/sql.cue new file mode 100644 index 0000000..63baaf3 --- /dev/null +++ b/cue/common/proxy/sql.cue @@ -0,0 +1,63 @@ +// Copyright 2025 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package proxy + +import ( + "time" +) + +#MySQL: { + // params Connection parameters + params?: {[string]: string} + // max_allowed_packet Max packet size allowed + max_allowed_packet?: number + // timeout Dial timeout + timeout?: time.Duration + // read_timeout I/O read timeout + read_timeout?: time.Duration + // write_timeout I/O read timeout + write_timeout?: time.Duration +} + +#Postgres: { + // options specifies command-line options to send to the server at connection start + options?: string + // max_conns is the maximum size of the pool + max_conns?: number + // connect_timeout the timeout value used for socket connect operations. + connect_timeout?: time.Duration + // prepare_threshold specifies the number of PreparedStatement executions that must occur before the driver begins using server-side prepared statements. + prepare_threshold?: number + // ssl_mode to use when connecting to postgres + ssl_mode?: "disable" | "allow" | "prefer" | "require" | "verify-ca" | "verify-full" +} + +#SQLProxy: { + kind: "SQLProxy" + spec: { + driver: "mysql" | "postgres" + // host is the hostname and port of the datasource. It is not the hostname of the proxy. + // The Perses server is the proxy, so it needs to know where to redirect the request. + host: string + // database is the name of the database to connect to + database: string + // secret is the name of the secret that should be used for the proxy or discovery configuration + // It will contain any sensitive information such as username, password, token, certificate. + secret?: string + // mysql specific driver configurations + mysql?: #MySQL + // postgres specific driver configurations + postgres?: #Postgres + } +} diff --git a/cue/common/threshold.cue b/cue/common/threshold.cue new file mode 100644 index 0000000..12f010b --- /dev/null +++ b/cue/common/threshold.cue @@ -0,0 +1,26 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package common + +#stepOption: { + value: number + color?: string + name?: string +} + +#thresholds: { + mode?: "percent" | "absolute" + defaultColor?: string + steps?: [...#stepOption] +} diff --git a/cue/common/transform.cue b/cue/common/transform.cue new file mode 100644 index 0000000..9815346 --- /dev/null +++ b/cue/common/transform.cue @@ -0,0 +1,52 @@ +// Copyright 2024 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package common + +import ( + "strings" +) + +#joinByColumnValueTransform: { + kind: "JoinByColumnValue" + spec: { + columns: [...string] + disabled?: bool + } +} + +#mergeColumnsTransform: { + kind: "MergeColumns" + spec: { + columns: [...string] + name: strings.MinRunes(1) + disabled?: bool + } +} + +#mergeIndexedColumnsTransform: { + kind: "MergeIndexedColumns" + spec: { + column: strings.MinRunes(1) + disabled?: bool + } +} + +#mergeSeries: { + kind: "MergeSeries" + spec: { + disabled?: bool + } +} + +#transform: #joinByColumnValueTransform | #mergeColumnsTransform | #mergeIndexedColumnsTransform | #mergeSeries diff --git a/cue/common/url.cue b/cue/common/url.cue new file mode 100644 index 0000000..514c6ee --- /dev/null +++ b/cue/common/url.cue @@ -0,0 +1,16 @@ +// Copyright 2023 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package common + +#url: =~"^https?:\/\/[^\\s\/$.?#].[^\\s]*$" diff --git a/cue/common/variable.cue b/cue/common/variable.cue new file mode 100644 index 0000000..d02c731 --- /dev/null +++ b/cue/common/variable.cue @@ -0,0 +1,16 @@ +// Copyright 2025 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package common + +#variableSyntaxRegex: "^\\$\\w+$" diff --git a/cue/cue.mod/module.cue b/cue/cue.mod/module.cue new file mode 100644 index 0000000..cd9a64f --- /dev/null +++ b/cue/cue.mod/module.cue @@ -0,0 +1,7 @@ +module: "github.com/perses/shared/cue@v0" +language: { + version: "v0.14.0" +} +source: { + kind: "git" +} diff --git a/scripts/validate-cue/validate-cue.go b/scripts/validate-cue/validate-cue.go new file mode 100644 index 0000000..7df2711 --- /dev/null +++ b/scripts/validate-cue/validate-cue.go @@ -0,0 +1,129 @@ +// Copyright 2025 The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + + "github.com/sirupsen/logrus" +) + +// This script goes through the CUE files and validates each of them against its +// corresponding test file, if it exists. + +const ( + schemasDir = "cue" + testDir = "cue-test" +) + +// dirsInScope specifies which subdirectories under cue/ to validate +var dirsInScope = []string{"common"} + +func findCueFiles(baseDir string, subDir string) ([]string, error) { + var files []string + dirPath := filepath.Join(baseDir, subDir) + + err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if !info.IsDir() && filepath.Ext(path) == ".cue" { + // Convert to relative path from baseDir + relPath, err := filepath.Rel(baseDir, path) + if err != nil { + return err + } + files = append(files, relPath) + } + + return nil + }) + + return files, err +} + +func fileExists(path string) bool { + _, err := os.Stat(path) + return !os.IsNotExist(err) +} + +func runCueVet(schemaFile, testFile string) error { + logrus.Debugf("Validating %s against %s", schemaFile, testFile) + + cmd := exec.Command("cue", "vet", "-c", schemaFile, testFile) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to validate %s: %w", schemaFile, err) + } + + return nil +} + +func validateCueFiles() error { + logrus.Debugf("Starting CUE files validation") + + // Check if cue command is available + if _, err := exec.LookPath("cue"); err != nil { + return fmt.Errorf("cue command not found in PATH: %w", err) + } + + validatedCount := 0 + skippedCount := 0 + errCount := 0 + + for _, subDir := range dirsInScope { + logrus.Debugf("Processing directory: %s", subDir) + + files, err := findCueFiles(schemasDir, subDir) + if err != nil { + return fmt.Errorf("failed to find CUE files in %s/%s: %w", schemasDir, subDir, err) + } + + for _, file := range files { + schemaFile := filepath.Join(schemasDir, file) + testFile := filepath.Join(testDir, file) + + if !fileExists(testFile) { + logrus.Debugf("Skipping %s: test file %s not found", schemaFile, testFile) + skippedCount++ + continue + } + + if err := runCueVet(schemaFile, testFile); err != nil { + logrus.Errorf("Validation failed for %s: %v", schemaFile, err) + errCount++ + } + + validatedCount++ + } + } + if errCount > 0 { + return fmt.Errorf("validation failed for %d file(s)", errCount) + } + + logrus.Infof("CUE files validation completed: %d validated, %d skipped", validatedCount, skippedCount) + return nil +} + +func main() { + if err := validateCueFiles(); err != nil { + logrus.Fatal(err) + } +} From 48b176ab2ce03bd5a54ba812d9f9ae149f163c40 Mon Sep 17 00:00:00 2001 From: Antoine THEBAUD Date: Fri, 19 Dec 2025 12:08:55 +0100 Subject: [PATCH 2/3] CI integration & path fix Signed-off-by: Antoine THEBAUD --- .github/workflows/cue.yml | 53 +++++++++++++++++++ Makefile | 25 +++++++++ cue/common/proxy/http.cue | 2 +- cue/cue.mod/module.cue | 2 +- .../validate-cue.go => test-cue/test-cue.go} | 0 5 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/cue.yml create mode 100644 Makefile rename scripts/{validate-cue/validate-cue.go => test-cue/test-cue.go} (100%) diff --git a/.github/workflows/cue.yml b/.github/workflows/cue.yml new file mode 100644 index 0000000..4c01179 --- /dev/null +++ b/.github/workflows/cue.yml @@ -0,0 +1,53 @@ +name: cue +on: + push: + branches: + - main + - release/* + - snapshot/* + tags: + - v* + pull_request: + merge_group: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' || github.ref_name != 'main' }} + +jobs: + validate-schemas: + name: validate CUE schemas + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v6 + - uses: perses/github-actions@v0.11.0 + - uses: ./.github/perses-ci/actions/setup_environment + with: + enable_go: true + enable_cue: true + cue_version: "v0.15.1" + - name: check that CUE schemas evaluate correctly + run: make cue-eval + - name: run the unit tests for CUE schemas + run: make cue-test + + publish-module: + name: "publish CUE module to Central Registry" + needs: "validate-schemas" + runs-on: ubuntu-latest + if: ${{ github.event_name == 'push' && startsWith(github.ref_name, 'v') }} + steps: + - name: Checkout + uses: actions/checkout@v6 + - uses: perses/github-actions@v0.11.0 + - uses: ./.github/perses-ci/actions/setup_environment + with: + enable_go: true + enable_cue: true + cue_version: "v0.15.1" + - name: Login to Central Registry # to allow publishing the module + run: cue login --token=${{ secrets.CUE_REG_TOKEN }} + - name: Publish the module + run: cue mod publish ${{ github.ref_name }} + working-directory: cue \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8e79ea9 --- /dev/null +++ b/Makefile @@ -0,0 +1,25 @@ +# Copyright 2025 The Perses Authors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +GO ?= go +CUE ?= cue + +.PHONY: cue-eval +cue-eval: + @echo ">> Check that CUE schemas evaluate correctly" + cd cue && $(CUE) eval ./... + +.PHONY: cue-test +cue-test: + @echo ">> Run the unit tests for CUE schemas" + $(GO) run ./scripts/test-cue/test-cue.go \ No newline at end of file diff --git a/cue/common/proxy/http.cue b/cue/common/proxy/http.cue index e23179e..1e55591 100644 --- a/cue/common/proxy/http.cue +++ b/cue/common/proxy/http.cue @@ -14,7 +14,7 @@ package proxy import ( - "github.com/perses/perses/cue/common" + "github.com/perses/shared/cue/common" ) #HTTPAllowedEndpoint: { diff --git a/cue/cue.mod/module.cue b/cue/cue.mod/module.cue index cd9a64f..aa90f36 100644 --- a/cue/cue.mod/module.cue +++ b/cue/cue.mod/module.cue @@ -1,6 +1,6 @@ module: "github.com/perses/shared/cue@v0" language: { - version: "v0.14.0" + version: "v0.15.1" } source: { kind: "git" diff --git a/scripts/validate-cue/validate-cue.go b/scripts/test-cue/test-cue.go similarity index 100% rename from scripts/validate-cue/validate-cue.go rename to scripts/test-cue/test-cue.go From 38daacc7fe82c3821324855d7ec6867a588d161f Mon Sep 17 00:00:00 2001 From: Antoine THEBAUD Date: Fri, 19 Dec 2025 15:08:02 +0100 Subject: [PATCH 3/3] fix CI condition Signed-off-by: Antoine THEBAUD --- .github/workflows/cue.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cue.yml b/.github/workflows/cue.yml index 4c01179..1aa8bfe 100644 --- a/.github/workflows/cue.yml +++ b/.github/workflows/cue.yml @@ -36,7 +36,7 @@ jobs: name: "publish CUE module to Central Registry" needs: "validate-schemas" runs-on: ubuntu-latest - if: ${{ github.event_name == 'push' && startsWith(github.ref_name, 'v') }} + if: ${{ github.event.release.tag_name }} steps: - name: Checkout uses: actions/checkout@v6