Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
230ac61
[dmt] add markdownlint documentation rule
fuldaxxx Jun 17, 2026
304b100
chore: use deckhouse markdownlint config
fuldaxxx Jun 22, 2026
205584d
add go-markdownlint as direct dependency
fuldaxxx Jun 22, 2026
77f6103
add markdownlint rule to documentation linter
fuldaxxx Jun 24, 2026
f301df6
exclude readme files from markdown lint
fuldaxxx Jun 24, 2026
af886ff
[fix] use forked markdownlint and lint readmes
fuldaxxx Jun 26, 2026
e787d3b
[dmt] add markdownlint violation fixture
fuldaxxx Jun 26, 2026
935ab0f
[chore] add markdownlint dependency
fuldaxxx Jun 26, 2026
e1061e2
[dmt] fix: switch markdownlint dependency and add e2e coverage
fuldaxxx Jul 10, 2026
563aeec
[chore] add markdownlint dependency
fuldaxxx Jul 10, 2026
3f320fd
[chore] replace markdownlint dependency
fuldaxxx Jul 10, 2026
7b72b68
[chore] remove local go-markdownlint replace
fuldaxxx Jul 10, 2026
8534f52
[chore] bump go-markdownlint to v0.0.4
fuldaxxx Jul 10, 2026
21bd115
[fix] disable md051 in markdown lint
fuldaxxx Jul 12, 2026
6211a9f
[fix] exclude readme from markdownlint scan
fuldaxxx Jul 12, 2026
e0a69fa
[chore] replace markdownlint all-rules fixture
fuldaxxx Jul 12, 2026
90f9d7d
[dmt] chore: set markdownlint warn by default
fuldaxxx Jul 15, 2026
d930570
[dmt] fix: default markdownlint to warn
fuldaxxx Jul 15, 2026
33ddc31
[chore] remove markdownlint e2e test fixtures
fuldaxxx Jul 15, 2026
2e9b73e
[chore] comment out markdownlint rules
fuldaxxx Jul 15, 2026
e29b362
[fix] docs: document markdownlint rules
fuldaxxx Jul 15, 2026
1a3667b
[dmt] chore: document markdownlint rule behavior
fuldaxxx Jul 16, 2026
985758b
Merge branch 'main' into feat/markdown-lint
ldmonster Jul 21, 2026
e6cd4f9
[chore] replace markdownlint dependency
fuldaxxx Jul 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ DMT includes **9 specialized linters** to validate different aspects of your Dec
| Linter | Purpose | Key Checks |
|--------|---------|------------|
| [**Container**](pkg/linters/container/README.md) | Container configuration validation | Duplicate names, env vars, security contexts, probes, resource limits, mount-points |
| [**Documentation**](pkg/linters/docs/README.md) | Documentation quality | README presence, bilingual support, no cyrillic in English docs |
| [**Documentation**](pkg/linters/docs/README.md) | Documentation quality | README presence, bilingual support, no cyrillic in English docs, markdown style |
| [**Hooks**](pkg/linters/hooks/README.md) | Hook validation | Hook syntax, ingress configurations |
| [**Images**](pkg/linters/images/README.md) | Image build instructions | Dockerfile best practices, werf configuration |
| [**Module**](pkg/linters/module/README.md) | Module structure | module.yaml format, OpenAPI conversions, oss.yaml, license files |
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ require (
github.com/iancoleman/strcase v0.3.0
github.com/itchyny/gojq v0.12.19
github.com/kyokomi/emoji v2.2.4+incompatible
github.com/ldmonster/go-markdownlint v0.0.2
github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/go-wordwrap v1.0.1
github.com/mitchellh/mapstructure v1.5.0
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJ
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ=
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/Code-Hex/go-generics-cache v1.5.1 h1:6vhZGc5M7Y/YD8cIUcY8kcuQLB4cHR7U+0KMqAA0KcU=
Expand Down Expand Up @@ -273,6 +274,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/kyokomi/emoji v2.2.4+incompatible h1:np0woGKwx9LiHAQmwZx79Oc0rHpNw3o+3evou4BEPv4=
github.com/kyokomi/emoji v2.2.4+incompatible/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA=
github.com/ldmonster/go-markdownlint v0.0.2 h1:Szznt3Ua53TinxKPKlSY7iw5BmEPANg9+6ojtPrQYm0=
github.com/ldmonster/go-markdownlint v0.0.2/go.mod h1:d501SE7F9c34YnMsZWiPJikJ/hcl6PgS9y3bKe62cwY=
github.com/linode/linodego v1.46.0 h1:+uOG4SD2MIrhbrLrvOD5HrbdLN3D19Wgn3MgdUNQjeU=
github.com/linode/linodego v1.46.0/go.mod h1:vyklQRzZUWhFVBZdYx4dcYJU/gG9yKB9VUcUs6ub0Lk=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
Expand Down
5 changes: 5 additions & 0 deletions internal/module/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,11 @@ func mapDocumentationRules(linterSettings *pkg.LintersSettings, configSettings *
rules.ReadmeRule.SetLevel(globalRules.ReadmeRule.Impact, fallbackImpact)
rules.CyrillicInEnglishRule.SetLevel(globalRules.NoCyrillicExcludeRules.Impact, fallbackImpact)
rules.NoLangKeyRule.SetLevel(globalRules.NoLangKeyRule.Impact, fallbackImpact)
// markdownlint defaults to warn (non-fatal) rather than error. Unlike the
// other documentation rules, the linter-level impact (fallbackImpact) is
// intentionally NOT used as the fallback here — only an explicit rule-level
// markdownlint impact overrides the warn default.
rules.MarkdownlintRule.SetLevel(globalRules.MarkdownlintRule.Impact, pkg.Warn.String())
}

func mapModuleRules(linterSettings *pkg.LintersSettings, configSettings *config.LintersSettings, globalConfig *global.Linters) {
Expand Down
1 change: 1 addition & 0 deletions pkg/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ type DocumentationLinterRules struct {
BilingualRule RuleConfig
CyrillicInEnglishRule RuleConfig
NoLangKeyRule RuleConfig
MarkdownlintRule RuleConfig
}

type NoCyrillicLinterConfig struct {
Expand Down
1 change: 1 addition & 0 deletions pkg/config/global/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ type DocumentationRules struct {
ReadmeRule RuleConfig `mapstructure:"readme"`
NoCyrillicExcludeRules RuleConfig `mapstructure:"cyrillic-in-english"`
NoLangKeyRule RuleConfig `mapstructure:"no-lang-key"`
MarkdownlintRule RuleConfig `mapstructure:"markdownlint"`
}

type OpenAPILinterConfig struct {
Expand Down
126 changes: 125 additions & 1 deletion pkg/linters/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Overview

The **Documentation Linter** validates module documentation to ensure proper structure, completeness, and language consistency. This linter enforces bilingual documentation requirements, checks for documentation file presence, and validates that English documentation doesn't contain cyrillic characters.
The **Documentation Linter** validates module documentation to ensure proper structure, completeness, and language consistency. This linter enforces bilingual documentation requirements, checks for documentation file presence, validates that English documentation doesn't contain cyrillic characters, and ensures markdown files follow deckhouse markdown style conventions.

Proper documentation is critical for Deckhouse modules as it helps users understand module features, configuration options, and usage patterns. The linter ensures documentation meets quality standards and is accessible to both English and Russian-speaking audiences.

Expand All @@ -14,6 +14,7 @@ Proper documentation is critical for Deckhouse modules as it helps users underst
| [bilingual](#bilingual) | Validates documentation exists in both English and Russian | ✅ | enabled |
| [cyrillic-in-english](#cyrillic-in-english) | Validates English documentation doesn't contain cyrillic characters | ✅ | enabled |
| [no-lang-key](#no-lang-key) | Validates documentation front matter doesn't contain `lang` key | ✅ | enabled |
| [markdownlint](#markdownlint) | Validates markdown files in docs/ follow deckhouse markdown style | ✅ | enabled |

"Configurable" means that this rule can be configured using the `.dmtlint.yaml` file, including customizing the rule's parameters and/or disabling the rule.

Expand Down Expand Up @@ -388,6 +389,129 @@ linters-settings:

---

### markdownlint

**Purpose:** Ensures markdown files in the `docs/` directory follow consistent deckhouse markdown style conventions (headings, lists, code blocks, etc.).

**Description:**

This rule runs the [go-markdownlint](https://github.com/ldmonster/go-markdownlint) library against every `.md` file under `docs/` (recursively, including `docs/internal/...`) and reports any markdown style violations. The built-in rule set is enabled by default; only a fixed set of deckhouse-specific overrides is applied (line-length limits, blanks-around-headings, duplicate-heading siblings, etc.).

Unlike the other documentation rules, `markdownlint` reports at `warn` **by default** — its findings are shown but do not fail the run. Set `impact: error` to make violations fatal.

**What it checks:**

1. Recursively scans all `.md` files under `docs/` (top-level and nested, e.g. `docs/internal/`)
2. Lints each file with the built-in markdownlint rules using the deckhouse configuration overrides
3. Reports the rule name(s), description, file path and line number for each violation

**Why it matters:**

Consistent markdown style across all modules makes the documentation easier to read, review and maintain, and keeps it aligned with the rest of the deckhouse documentation.

**Rule reference:**

Findings are reported as `MDxxx/rule-name …`. Look up the code below to see what it means. Rules marked *(tuned)* use deckhouse-specific settings.

| Rule (as shown in the error) | What it means |
|------------------------------|---------------|
| MD001 / heading-increment | Heading levels must increase one at a time — no jump from `#` to `###`. |
| MD003 / heading-style | Heading style must be consistent (ATX `#`, not closed `# … #` or setext). |
| MD005 / list-indent | List items at the same level must share the same indentation. |
| MD007 / ul-indent | Nested bullet lists must be indented by the expected amount. |
| MD009 / no-trailing-spaces | No trailing spaces at the end of a line. |
| MD010 / no-hard-tabs | No hard tabs — use spaces. |
| MD011 / no-reversed-links | Reversed link syntax `(text)[url]` instead of `[text](url)`. |
| MD012 / no-multiple-blanks | No multiple consecutive blank lines. |
| MD013 / line-length *(tuned)* | Line too long. Limits: 1000 chars (headings 128, code blocks 400). |
| MD014 / commands-show-output | `$` before shell commands only when their output is shown. |
| MD018 / no-missing-space-atx | Space required after `#` in a heading (`# Title`, not `#Title`). |
| MD019 / no-multiple-space-atx | At most one space after `#` in a heading. |
| MD020 / no-missing-space-closed-atx | Space required inside a closed heading `# Title #`. |
| MD021 / no-multiple-space-closed-atx | At most one space inside a closed heading. |
| MD022 / blanks-around-headings *(tuned)* | Headings must be surrounded by blank lines (1 above, 1 below). |
| MD023 / heading-start-left | Headings must start at the beginning of the line (no indent). |
| MD024 / no-duplicate-heading *(tuned)* | No duplicate heading text — checked among sibling headings only. |
| MD025 / single-title / single-h1 | Only one top-level (`#`) heading per document. |
| MD026 / no-trailing-punctuation *(tuned)* | No trailing punctuation in headings (`. , ; : !` and CJK variants). |
| MD027 / no-multiple-space-blockquote | At most one space after `>` in a blockquote. |
| MD028 / no-blanks-blockquote | No blank line inside a blockquote (it splits it in two). |
| MD029 / ol-prefix *(tuned)* | Ordered-list numbering — all `1.` or strictly ascending (`one_or_ordered`). |
| MD030 / list-marker-space | Correct number of spaces after a list marker. |
| MD031 / blanks-around-fences | Fenced code blocks must be surrounded by blank lines. |
| MD034 / no-bare-urls | Bare URLs must be wrapped in `<…>` or `[text](url)`. |
| MD035 / hr-style | Horizontal-rule style must be consistent (e.g. always `---`). |
| MD036 / no-emphasis-as-heading | Don't use bold/italic text in place of a heading. |
| MD037 / no-space-in-emphasis | No spaces inside emphasis markers (`**bold**`, not `** bold **`). |
| MD038 / no-space-in-code | No spaces inside inline code (`` `code` ``, not `` ` code ` ``). |
| MD039 / no-space-in-links | No spaces inside link text (`[link]`, not `[ link ]`). |
| MD040 / fenced-code-language | Fenced code blocks must declare a language after the opening fence (e.g. `yaml`, `bash`). |
| MD041 / first-line-heading / first-line-h1 *(tuned)* | First line must be a top-level heading (front-matter `title` counts). |
| MD042 / no-empty-links | No empty links (`[text]()`). |
| MD045 / no-alt-text | Images must have alt text (`![alt](img.png)`). |
| MD046 / code-block-style | Code-block style must be consistent within a file (fenced vs indented). |
| MD047 / single-trailing-newline | File must end with exactly one newline. |
| MD048 / code-fence-style | Code-fence style must be consistent (all fences use backticks, or all use tildes `~~~`). |
| MD049 / emphasis-style | Italic style must be consistent (`*` or `_`). |
| MD050 / strong-style | Bold style must be consistent (`**` or `__`). |
| MD052 / reference-links-images | Reference links/images must point to a defined label. |
| MD053 / link-image-reference-definitions | Reference definitions (`[label]: url`) must be used — no unused ones. |
| MD055 / table-pipe-style | Table leading/trailing pipe (`\|`) style must be consistent. |
| MD056 / table-column-count | Every table row must have the same number of columns. |
| MD058 / blanks-around-tables | Tables must be surrounded by blank lines. |
| MD059 / descriptive-link-text | Link text must be descriptive — not `here`, `link`, `click here`. |

Three rules are enabled but effectively inert under the deckhouse config, so you will not see them fire: **MD043** (required-headings — no required structure is set), **MD044** (proper-names — the name list is empty) and **MD054** (link-image-style — all link/image styles are allowed by default).

Rules **disabled** on purpose (never reported): MD002 (first-heading-h1, deprecated), MD004 (ul-style), MD032 (blanks-around-lists), MD033 (no-inline-html — HTML is allowed), MD051 (link-fragments — Deckhouse anchors only exist after the doc build), MD060 (table-column-style).

**Examples:**

❌ **Incorrect** - Duplicate top-level heading (MD025) and missing trailing newline (MD047):

```markdown
<!-- docs/README.md -->
# My Module

# My Module
```

(file has no trailing newline)

**Error:**
```
MD025/single-title/single-h1 Multiple top-level headings in the same document
File: docs/README.md
Line: 3

MD047/single-trailing-newline Files should end with a single newline character
File: docs/README.md
Line: 3
```

✅ **Correct** - Single top-level heading and trailing newline:

```markdown
<!-- docs/README.md -->
# My Module
```

**Configuration:**

To make this rule fatal, or to disable it:

```yaml
# .dmtlint.yaml
linters-settings:
documentation:
rules:
markdownlint:
impact: error # fail the run on violations (default is warn)
# impact: ignored # disable the rule entirely
```

---

## Configuration

The Documentation linter can be configured at both the module level and for individual rules.
Expand Down
2 changes: 2 additions & 0 deletions pkg/linters/docs/documentation.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ func (l *Documentation) Run(m *module.Module) {
rules.NewCyrillicInEnglishRule().CheckFiles(m, errorList.WithMaxLevel(l.cfg.Rules.CyrillicInEnglishRule.GetLevel()))

rules.NewNoLangKeyRule().CheckFiles(m, errorList.WithMaxLevel(l.cfg.Rules.NoLangKeyRule.GetLevel()))

rules.NewMarkdownRule().CheckFiles(m, errorList.WithMaxLevel(l.cfg.Rules.MarkdownlintRule.GetLevel()))
}

func (l *Documentation) Name() string {
Expand Down
Loading
Loading