From a9b7f643895ee0bb08f6496403960cbed8a5d73a Mon Sep 17 00:00:00 2001 From: Artem Kuleshov Date: Mon, 6 Jul 2026 13:29:42 +0300 Subject: [PATCH] [dmt] version-aware disable.message / disable.messages lint Signed-off-by: Artem Kuleshov --- pkg/linters/module/README.md | 7 +- pkg/linters/module/rules/module_yaml.go | 27 +++++-- pkg/linters/module/rules/module_yaml_test.go | 70 ++++++++++++++++--- test/e2e/README.md | 5 +- .../disable-message-deprecated/expected.yaml | 10 --- .../disable-message-fallback/expected.yaml | 10 +++ .../module/module.yaml | 14 ++++ .../module/openapi/config-values.yaml | 0 .../module/openapi/values.yaml | 0 .../expected.yaml | 10 +++ .../module/module.yaml | 2 + .../module/openapi/config-values.yaml | 2 + .../module/openapi/values.yaml | 4 ++ .../disable-messages-invalid/expected.yaml | 6 +- 14 files changed, 134 insertions(+), 33 deletions(-) delete mode 100644 test/e2e/testdata/module/disable-message-deprecated/expected.yaml create mode 100644 test/e2e/testdata/module/disable-message-fallback/expected.yaml create mode 100644 test/e2e/testdata/module/disable-message-fallback/module/module.yaml rename test/e2e/testdata/module/{disable-message-deprecated => disable-message-fallback}/module/openapi/config-values.yaml (100%) rename test/e2e/testdata/module/{disable-message-deprecated => disable-message-fallback}/module/openapi/values.yaml (100%) create mode 100644 test/e2e/testdata/module/disable-message-on-supported-version/expected.yaml rename test/e2e/testdata/module/{disable-message-deprecated => disable-message-on-supported-version}/module/module.yaml (90%) create mode 100644 test/e2e/testdata/module/disable-message-on-supported-version/module/openapi/config-values.yaml create mode 100644 test/e2e/testdata/module/disable-message-on-supported-version/module/openapi/values.yaml diff --git a/pkg/linters/module/README.md b/pkg/linters/module/README.md index 4558cc5e..26ec5c73 100644 --- a/pkg/linters/module/README.md +++ b/pkg/linters/module/README.md @@ -42,9 +42,10 @@ Validates the `module.yaml` configuration file structure and content. - ✅ Update section follows versioning rules - ✅ `weight` must not be zero for critical modules - ✅ `description` field is deprecated (use `descriptions.en` instead) -- ✅ `disable.messages` (localized `ru`/`en`) requires `requirements.deckhouse` to be `>= 1.77` — otherwise an **error** (Deckhouse below 1.77 does not understand it) -- ✅ `disable.messages` must define **both** `ru` and `en` — otherwise an **error** -- ✅ `disable.message` without `disable.messages` is deprecated for Deckhouse `>= 1.77` — a **warning** pointing authors to the localized `disable.messages` +- ✅ `disable.messages` (localized `ru`/`en`) must define **both** `ru` and `en` — otherwise an **error** +- ✅ `disable.messages` on a module that allows Deckhouse `< 1.77` needs a `disable.message` fallback (or `requirements.deckhouse` pinned to `>= 1.77`) — otherwise an **error** (Deckhouse below 1.77 does not read `disable.messages`) +- ✅ `disable.message` on a module pinned to Deckhouse `>= 1.77` must be removed (only `disable.messages` is read there) — otherwise an **error**; a lone `disable.message` on `< 1.77` is the correct field, so no finding +- ✅ `disable.message` kept alongside `disable.messages` while the module still allows Deckhouse `< 1.77` — a **warning**: it is the `< 1.77` fallback and must be removed once `requirements.deckhouse` is pinned to `>= 1.77` #### Stage Values diff --git a/pkg/linters/module/rules/module_yaml.go b/pkg/linters/module/rules/module_yaml.go index e63e0f19..f06668f3 100644 --- a/pkg/linters/module/rules/module_yaml.go +++ b/pkg/linters/module/rules/module_yaml.go @@ -302,17 +302,30 @@ func (r *DefinitionFileRule) CheckDefinitionFile(modulePath string, errorList *e hasEn := yml.Disable.Messages.English != "" hasMessages := hasRu || hasEn - switch { - case hasMessages: - if !requirementsGuaranteeDeckhouse177(yml.Requirements) { - errorList.Error("Field 'disable.messages' is only supported on Deckhouse >= v1.77; set 'requirements.deckhouse' to \">= 1.77\"") - } + guaranteed := requirementsGuaranteeDeckhouse177(yml.Requirements) + + // disable.message is only read by Deckhouse < v1.77; a module limited to >= v1.77 + // must drop it in favour of the localized disable.messages. + if hasMessage && guaranteed { + errorList.Error("Field 'disable.message' must be removed on Deckhouse >= v1.77; use 'disable.messages' (with 'ru'/'en') instead") + } + if hasMessages { if !hasRu || !hasEn { errorList.Error("Field 'disable.messages' must define both 'ru' and 'en'") } - case hasMessage: - errorList.Warn("Field 'disable.message' is deprecated on Deckhouse >= v1.77, use 'disable.messages' (with 'ru'/'en') instead") + + // disable.messages is not read by Deckhouse < v1.77, so a module that can run + // there needs a disable.message fallback (or must pin requirements.deckhouse >= v1.77). + if !guaranteed && !hasMessage { + errorList.Error("Field 'disable.messages' is not read on Deckhouse < v1.77; add a 'disable.message' fallback or require 'requirements.deckhouse' >= v1.77") + } + + // Both are present while the module still targets < v1.77: disable.message is the + // fallback for those old versions and must be dropped once the module requires >= v1.77. + if hasMessage && !guaranteed { + errorList.Warn("Field 'disable.message' is a Deckhouse < v1.77 fallback and must be removed once 'requirements.deckhouse' is pinned to >= v1.77 (only 'disable.messages' is read there)") + } } } diff --git a/pkg/linters/module/rules/module_yaml_test.go b/pkg/linters/module/rules/module_yaml_test.go index d020bbc4..f03e7585 100644 --- a/pkg/linters/module/rules/module_yaml_test.go +++ b/pkg/linters/module/rules/module_yaml_test.go @@ -485,20 +485,37 @@ func TestCheckDefinitionFile_DeprecatedDisableMessageField(t *testing.T) { return false } - // disable.message without disable.messages -> deprecation warning, not a hard error. + // message only, pinned >= 1.77 -> error (must be removed in favour of messages). errorList := check(t, ` name: test-module stage: Experimental descriptions: en: "Test description" +requirements: + deckhouse: ">= 1.77" disable: confirmation: true message: "Old disable message field" `) - assert.False(t, errorList.ContainsErrors(), "disable.message deprecation must be a warning, not an error") - assert.True(t, containsText(errorList, "Field 'disable.message' is deprecated"), "expected disable.message deprecation warning") + assert.True(t, errorList.ContainsErrors(), "disable.message on >= 1.77 must be an error") + assert.True(t, containsText(errorList, "'disable.message' must be removed on Deckhouse >= v1.77"), "expected disable.message removal error") - // disable.messages without requirements.deckhouse >= 1.77 -> error. + // message only, requirements allow < 1.77 -> silent (messages is not available there). + errorList = check(t, ` +name: test-module +stage: Experimental +descriptions: + en: "Test description" +requirements: + deckhouse: ">= 1.60" +disable: + confirmation: true + message: "Old disable message field" +`) + assert.False(t, errorList.ContainsErrors(), "disable.message on < 1.77 must not error") + assert.False(t, containsText(errorList, "disable.message"), "disable.message on < 1.77 must be silent") + + // messages without a message fallback on < 1.77 -> error. errorList = check(t, ` name: test-module stage: Experimental @@ -510,10 +527,29 @@ disable: en: "Disable message" ru: "Сообщение при отключении" `) - assert.True(t, errorList.ContainsErrors(), "disable.messages without requirements.deckhouse >= 1.77 must be an error") - assert.True(t, containsText(errorList, "disable.messages' is only supported on Deckhouse >= v1.77"), "expected disable.messages requirement error") + assert.True(t, errorList.ContainsErrors(), "disable.messages without a message fallback on < 1.77 must be an error") + assert.True(t, containsText(errorList, "is not read on Deckhouse < v1.77"), "expected messages fallback error") - // disable.messages with requirements.deckhouse >= 1.77 -> clean. + // message + messages while the module still targets < 1.77 -> warning (fallback must be + // removed on >= 1.77), but not an error. + errorList = check(t, ` +name: test-module +stage: Experimental +descriptions: + en: "Test description" +requirements: + deckhouse: ">= 1.60" +disable: + confirmation: true + message: "Fallback for < 1.77" + messages: + en: "Disable message" + ru: "Сообщение при отключении" +`) + assert.False(t, errorList.ContainsErrors(), "disable.message fallback on < 1.77 must not be an error") + assert.True(t, containsText(errorList, "must be removed once 'requirements.deckhouse' is pinned to >= v1.77"), "expected disable.message fallback warning") + + // messages with requirements.deckhouse >= 1.77 -> clean. errorList = check(t, ` name: test-module stage: Experimental @@ -529,7 +565,7 @@ disable: `) assert.False(t, errorList.ContainsErrors(), "disable.messages with requirements.deckhouse >= 1.77 must be clean") - // disable.messages missing one of ru/en -> error. + // messages missing one of ru/en -> error. errorList = check(t, ` name: test-module stage: Experimental @@ -544,6 +580,24 @@ disable: `) assert.True(t, errorList.ContainsErrors(), "disable.messages missing 'ru' must be an error") assert.True(t, containsText(errorList, "must define both 'ru' and 'en'"), "expected both-languages error") + + // message alongside messages, pinned >= 1.77 -> error (message is redundant, must be removed). + errorList = check(t, ` +name: test-module +stage: Experimental +descriptions: + en: "Test description" +requirements: + deckhouse: ">= 1.77" +disable: + confirmation: true + message: "Old disable message field" + messages: + en: "Disable message" + ru: "Сообщение при отключении" +`) + assert.True(t, errorList.ContainsErrors(), "disable.message alongside messages on >= 1.77 must be an error") + assert.True(t, containsText(errorList, "'disable.message' must be removed on Deckhouse >= v1.77"), "expected disable.message removal error") } func TestCheckDefinitionFile_FileErrors(t *testing.T) { diff --git a/test/e2e/README.md b/test/e2e/README.md index 002702ec..cc0d10f1 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -94,8 +94,9 @@ go test ./test/e2e/ -run 'TestE2E//' -v |------|-----------| | `container/bad-deployment` | container linter (labels, security context, probes, resources, image digest, duplicate env, seccomp) | | `module/missing-metadata` | module linter (definition-file, helmignore) + documentation linter (readme) | -| `module/disable-message-deprecated` | module linter `definition-file` (warning: `disable.message` without `disable.messages` is deprecated) | -| `module/disable-messages-invalid` | module linter `definition-file` (errors: `disable.messages` without `requirements.deckhouse >= 1.77`, and missing `ru`/`en`) | +| `module/disable-message-on-supported-version` | module linter `definition-file` (error: `disable.message` on a module pinned to `>= 1.77` must be removed, use `disable.messages`) | +| `module/disable-messages-invalid` | module linter `definition-file` (errors: `disable.messages` without a `disable.message` fallback on `< 1.77`, and missing `ru`/`en`) | +| `module/disable-message-fallback` | module linter `definition-file` (warning: `disable.message` + `disable.messages` on a module still allowing `< 1.77` — drop the fallback on `>= 1.77`) | | `no-cyrillic/in-template` | no-cyrillic linter (Cyrillic characters in a yaml file) | | `no-cyrillic/skip-russian-files` | no-cyrillic linter skips Russian localized files (`*.ru.yml`, `*.ru.yaml`, `*.ru.json`, `doc-ru-*.yml`) while still reporting a regular Cyrillic template | | `no-cyrillic/skip-filenames-extensions` | no-cyrillic linter skips every filename/path pattern (`doc-ru-*`, `*.ru.{yaml,yml,json,md,html}`, `*_RU.md`, `docs/site/_*`, `docs/documentation/_*`, `tools/spelling/*`, `openapi/conversions/*`, `module.yaml`, `i18n/*`, `ru.*`) and non-scanned extensions (`.txt`), reporting only one genuine Cyrillic template | diff --git a/test/e2e/testdata/module/disable-message-deprecated/expected.yaml b/test/e2e/testdata/module/disable-message-deprecated/expected.yaml deleted file mode 100644 index 6b177597..00000000 --- a/test/e2e/testdata/module/disable-message-deprecated/expected.yaml +++ /dev/null @@ -1,10 +0,0 @@ -description: > - A module whose module.yaml still uses the deprecated singular disable.message - field (without disable.messages) should get a deprecation warning from the - module linter, pointing authors to the localized disable.messages (ru/en). -module: module -expect: - - linter: module - rule: definition-file - level: warn - textContains: "Field 'disable.message' is deprecated" diff --git a/test/e2e/testdata/module/disable-message-fallback/expected.yaml b/test/e2e/testdata/module/disable-message-fallback/expected.yaml new file mode 100644 index 00000000..e6c42add --- /dev/null +++ b/test/e2e/testdata/module/disable-message-fallback/expected.yaml @@ -0,0 +1,10 @@ +description: > + A module that keeps both disable.message and disable.messages while still allowing + Deckhouse < 1.77 gets a warning: disable.message is the < 1.77 fallback and must be + removed once the module requires >= 1.77. +module: module +expect: + - linter: module + rule: definition-file + level: warn + textContains: "must be removed once 'requirements.deckhouse' is pinned to >= v1.77" diff --git a/test/e2e/testdata/module/disable-message-fallback/module/module.yaml b/test/e2e/testdata/module/disable-message-fallback/module/module.yaml new file mode 100644 index 00000000..6e5882ba --- /dev/null +++ b/test/e2e/testdata/module/disable-message-fallback/module/module.yaml @@ -0,0 +1,14 @@ +name: e2e-module-disable-fallback +namespace: e2e-module-disable-fallback +stage: Experimental +descriptions: + en: "Module used to verify the disable.message fallback warning" + ru: "Модуль для проверки предупреждения о fallback disable.message" +requirements: + deckhouse: ">= 1.60" +disable: + confirmation: true + message: "Fallback for Deckhouse < 1.77" + messages: + ru: "Локализованное сообщение отключения" + en: "Localized disable message" diff --git a/test/e2e/testdata/module/disable-message-deprecated/module/openapi/config-values.yaml b/test/e2e/testdata/module/disable-message-fallback/module/openapi/config-values.yaml similarity index 100% rename from test/e2e/testdata/module/disable-message-deprecated/module/openapi/config-values.yaml rename to test/e2e/testdata/module/disable-message-fallback/module/openapi/config-values.yaml diff --git a/test/e2e/testdata/module/disable-message-deprecated/module/openapi/values.yaml b/test/e2e/testdata/module/disable-message-fallback/module/openapi/values.yaml similarity index 100% rename from test/e2e/testdata/module/disable-message-deprecated/module/openapi/values.yaml rename to test/e2e/testdata/module/disable-message-fallback/module/openapi/values.yaml diff --git a/test/e2e/testdata/module/disable-message-on-supported-version/expected.yaml b/test/e2e/testdata/module/disable-message-on-supported-version/expected.yaml new file mode 100644 index 00000000..4714d394 --- /dev/null +++ b/test/e2e/testdata/module/disable-message-on-supported-version/expected.yaml @@ -0,0 +1,10 @@ +description: > + A module pinned to Deckhouse >= 1.77 must not keep the singular disable.message + field: on >= 1.77 only the localized disable.messages is read, so disable.message + must be removed. The module linter flags it as an error. +module: module +expect: + - linter: module + rule: definition-file + level: error + textContains: "'disable.message' must be removed on Deckhouse >= v1.77" diff --git a/test/e2e/testdata/module/disable-message-deprecated/module/module.yaml b/test/e2e/testdata/module/disable-message-on-supported-version/module/module.yaml similarity index 90% rename from test/e2e/testdata/module/disable-message-deprecated/module/module.yaml rename to test/e2e/testdata/module/disable-message-on-supported-version/module/module.yaml index 77be98ea..b14011de 100644 --- a/test/e2e/testdata/module/disable-message-deprecated/module/module.yaml +++ b/test/e2e/testdata/module/disable-message-on-supported-version/module/module.yaml @@ -4,6 +4,8 @@ stage: Experimental descriptions: en: "Module used to verify the disable.message deprecation lint" ru: "Модуль для проверки депрекации disable.message" +requirements: + deckhouse: ">= 1.77" disable: confirmation: true message: "Deprecated singular disable message" diff --git a/test/e2e/testdata/module/disable-message-on-supported-version/module/openapi/config-values.yaml b/test/e2e/testdata/module/disable-message-on-supported-version/module/openapi/config-values.yaml new file mode 100644 index 00000000..03b0d8bf --- /dev/null +++ b/test/e2e/testdata/module/disable-message-on-supported-version/module/openapi/config-values.yaml @@ -0,0 +1,2 @@ +type: object +properties: {} diff --git a/test/e2e/testdata/module/disable-message-on-supported-version/module/openapi/values.yaml b/test/e2e/testdata/module/disable-message-on-supported-version/module/openapi/values.yaml new file mode 100644 index 00000000..47180da5 --- /dev/null +++ b/test/e2e/testdata/module/disable-message-on-supported-version/module/openapi/values.yaml @@ -0,0 +1,4 @@ +x-extend: + schema: config-values.yaml +type: object +properties: {} diff --git a/test/e2e/testdata/module/disable-messages-invalid/expected.yaml b/test/e2e/testdata/module/disable-messages-invalid/expected.yaml index c00928b0..2c526366 100644 --- a/test/e2e/testdata/module/disable-messages-invalid/expected.yaml +++ b/test/e2e/testdata/module/disable-messages-invalid/expected.yaml @@ -1,13 +1,13 @@ description: > A module that sets disable.messages with only one language (en, no ru) and - without requirements.deckhouse >= 1.77 must be flagged with both errors: the - version requirement and the missing localization. + without a disable.message fallback (requirements allow Deckhouse < 1.77) must be + flagged with both errors: the missing fallback and the missing localization. module: module expect: - linter: module rule: definition-file level: error - textContains: "is only supported on Deckhouse >= v1.77" + textContains: "is not read on Deckhouse < v1.77" - linter: module rule: definition-file level: error