Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,13 +278,15 @@ remote:
bundle: # dmt lint remote <repo>:<tag>
documentation:
rules:
changelog:
readme:
impact: warn
release: # ... the same command, <repo>/release:<tag>
module:
rules:
release-layout:
impact: error
changelog-valid:
impact: error
```

A `remote` section has the same shape as `global.linters-settings` — a linter
Expand Down
46 changes: 2 additions & 44 deletions internal/metrics/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,55 +17,13 @@ limitations under the License.
package metrics

import (
"path/filepath"
"strings"

"gopkg.in/ini.v1"

"github.com/deckhouse/deckhouse/pkg/log"

"github.com/deckhouse/dmt/internal/fsutils"
"github.com/deckhouse/dmt/pkg"
)

func getRepositoryAddress(dir string) string {
configFile := getGitConfigFile(dir)
if configFile == "" {
return ""
}

cfg, err := ini.Load(configFile)
if err != nil {
log.Error("Failed to load config file", log.Err(err))
return ""
}

sec, err := cfg.GetSection("remote \"origin\"")
if err != nil {
log.Error("Failed to get remote origin", log.Err(err))
return ""
}

repositoryULR := sec.Key("url").String()

return convertToHTTPS(repositoryULR)
}

func getGitConfigFile(dir string) string {
for {
if fsutils.IsDir(filepath.Join(dir, ".git")) &&
fsutils.IsFile(filepath.Join(dir, ".git", "config")) {
return filepath.Join(dir, ".git", "config")
}

parent := filepath.Dir(dir)
if dir == parent || parent == "" {
break
}

dir = parent
}

return ""
return convertToHTTPS(pkg.RepositoryOriginURL(dir))
}

func convertToHTTPS(repoURL string) string {
Expand Down
3 changes: 2 additions & 1 deletion internal/modules/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,6 @@ func mapDocumentationRules(linterSettings *pkg.LintersSettings, configSettings *
// render), so it defaults to error via fallbackImpact — unlike the style/soft
// markdownlint and size rules above. A per-rule impact in config still overrides.
rules.FrontMatterRule.SetLevel(globalRules.FrontMatterRule.Impact, fallbackImpact)
rules.ChangelogRule.SetLevel(globalRules.ChangelogRule.Impact, fallbackImpact)
}

func mapModuleRules(linterSettings *pkg.LintersSettings, configSettings *config.LintersSettings, globalConfig *global.Linters) {
Expand All @@ -388,6 +387,8 @@ func mapModuleRules(linterSettings *pkg.LintersSettings, configSettings *config.
rules.ReleaseLayoutRule.SetLevel(globalRules.ReleaseLayoutRule.Impact, fallbackImpact)
rules.BundleLayoutRule.SetLevel(globalRules.BundleLayoutRule.Impact, fallbackImpact)
rules.HelmignoreCoverageRule.SetLevel(globalRules.HelmignoreCoverageRule.Impact, fallbackImpact)
rules.HasChangelogRule.SetLevel(globalRules.HasChangelogRule.Impact, fallbackImpact)
rules.ChangelogValidRule.SetLevel(globalRules.ChangelogValidRule.Impact, fallbackImpact)
}

// mapTemplatesRules configures Templates linter rules
Expand Down
6 changes: 2 additions & 4 deletions pkg/config.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
package pkg

// TODO: THINK ABOUT HOW TO ENDURE
var IgnoreDeckhouseReposList = []string{"deckhouse", "deckhouse-test-1", "deckhouse-test-2"}

type LinterConfig struct {
Impact *Level
}
Expand Down Expand Up @@ -90,7 +87,6 @@ type DocumentationLinterRules struct {
MarkdownlintRule RuleConfig
SizeRule RuleConfig
FrontMatterRule RuleConfig
ChangelogRule RuleConfig
}

type NoCyrillicLinterConfig struct {
Expand Down Expand Up @@ -253,6 +249,8 @@ type ModuleLinterRules struct {
ReleaseLayoutRule RuleConfig
BundleLayoutRule RuleConfig
HelmignoreCoverageRule RuleConfig
HasChangelogRule RuleConfig
ChangelogValidRule RuleConfig
}
type OSSRuleSettings struct {
Disable bool
Expand Down
3 changes: 2 additions & 1 deletion pkg/config/global/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ type DocumentationRules struct {
MarkdownlintRule RuleConfig `mapstructure:"markdownlint"`
SizeRule RuleConfig `mapstructure:"size"`
FrontMatterRule RuleConfig `mapstructure:"front-matter"`
ChangelogRule RuleConfig `mapstructure:"changelog"`
}

type OpenAPILinterConfig struct {
Expand Down Expand Up @@ -133,6 +132,8 @@ type ModuleLinterRules struct {
ReleaseLayoutRule RuleConfig `mapstructure:"release-layout"`
BundleLayoutRule RuleConfig `mapstructure:"bundle-layout"`
HelmignoreCoverageRule RuleConfig `mapstructure:"helmignore-coverage"`
HasChangelogRule RuleConfig `mapstructure:"has-changelog"`
ChangelogValidRule RuleConfig `mapstructure:"changelog-valid"`
}

type TemplatesLinterConfig struct {
Expand Down
1 change: 0 additions & 1 deletion pkg/linters/docs/documentation.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ func (l *Documentation) rules() []pkg.Rule {
rules.NewMarkdownRule(m, errorList.WithMaxLevel(l.cfg.Rules.MarkdownlintRule.GetLevel())),
rules.NewSizeRule(m, errorList.WithMaxLevel(l.cfg.Rules.SizeRule.GetLevel())),
rules.NewFrontMatterRule(m, errorList.WithMaxLevel(l.cfg.Rules.FrontMatterRule.GetLevel())),
rules.NewChangelogRule(m, errorList.WithMaxLevel(l.cfg.Rules.ChangelogRule.GetLevel())),
}
}

Expand Down
52 changes: 0 additions & 52 deletions pkg/linters/docs/rules/changelog.go

This file was deleted.

33 changes: 33 additions & 0 deletions pkg/linters/module/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ The Module linter includes the following validation rules:

| Rule | Description | Configurable |
|------|-------------|--------------|
| [**has-changelog**](#has-changelog) | Validates that a non-empty `changelog.yaml` is carried | ✅ Yes |
| [**changelog-valid**](#changelog-valid) | Validates that `changelog.yaml` parses | ✅ Yes |
| [**definition-file**](#definition-file) | Validates `module.yaml` structure, accessibility, and update sections | ✅ Yes |
| [**oss**](#oss) | Validates open-source software attribution in `oss.yaml` | ✅ Yes |
| [**conversions**](#conversions) | Validates OpenAPI conversion files and documentation | ✅ Yes |
Expand All @@ -29,6 +31,37 @@ The Module linter includes the following validation rules:

## Rule Details

### Has-changelog

Validates that a published image carries a `changelog.yaml` and that it is not empty.

**Purpose:** the changelog is what a release is described by. An image that ships without one publishes a release with nothing to show for it, and the gap is invisible until someone reads the release page.

**Checks:**

- ✅ `changelog.yaml` exists in the package root
- ✅ It is not zero bytes

**Scope:** `release` and `bundle` — the published images are where a changelog has to be, and the source tree is not checked for one. Whether it parses is checked by [changelog-valid](#changelog-valid), which runs in the same two scopes.

The layout rules already report the file missing in both scopes, so this rule's own contribution is the empty-file case.

A module built into the Deckhouse monorepo needs no exemption here: it publishes no images of its own — it rides the platform release, and its changes are described by the repo-level `CHANGELOG/` — so neither scope ever runs over it.

---

### Changelog-valid

Validates the `changelog.yaml` a published image ships.

**Purpose:** Deckhouse reads this file to render the release notes for a version. A file it cannot parse means the release lands with no notes at all, and nothing upstream of the registry catches it.

**Scope:** `release` and `bundle` — both published images carry `changelog.yaml`.

A missing `changelog.yaml` is not this rule's finding: the layout rules own presence and [has-changelog](#has-changelog) owns emptiness, and this rule stays quiet when the file is absent — the same split [definition-file](#definition-file) and [package-yaml](#package-yaml) follow.

---

### Definition file

Validates the `module.yaml` configuration file structure and content.
Expand Down
2 changes: 2 additions & 0 deletions pkg/linters/module/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ func (l *Module) rules() []pkg.Rule {
rules.NewReleaseLayoutRule(m, level(cfg.Rules.ReleaseLayoutRule)),
rules.NewBundleLayoutRule(m, level(cfg.Rules.BundleLayoutRule)),
rules.NewHelmignoreCoverageRule(m, level(cfg.Rules.HelmignoreCoverageRule)),
rules.NewHasChangelogRule(m, level(cfg.Rules.HasChangelogRule)),
rules.NewChangelogValidRule(m, level(cfg.Rules.ChangelogValidRule)),
}
}

Expand Down
76 changes: 76 additions & 0 deletions pkg/linters/module/rules/changelog_valid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
Copyright 2026 Flant JSC

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 rules

import (
"context"
"os"
"path/filepath"

"sigs.k8s.io/yaml"

"github.com/deckhouse/dmt/pkg"
"github.com/deckhouse/dmt/pkg/errors"
)

const (
ChangelogValidRuleName = "changelog-valid"
ChangelogFilename = "changelog.yaml"
)

// ChangelogValidRule reports a changelog.yaml the release image ships that cannot be parsed.
type ChangelogValidRule struct {
pkg.RuleMeta

module pkg.Module
errorList *errors.LintRuleErrorsList
}

var _ pkg.Rule = (*ChangelogValidRule)(nil)

func NewChangelogValidRule(m pkg.Module, errorList *errors.LintRuleErrorsList) *ChangelogValidRule {
return &ChangelogValidRule{
RuleMeta: pkg.RuleMeta{Name: ChangelogValidRuleName},
module: m,
errorList: errorList.WithRule(ChangelogValidRuleName),
}
}

func (r *ChangelogValidRule) Check(_ context.Context) {
root := r.module.GetPath()
if root == "" {
return
}

errorList := r.errorList.WithFilePath(ChangelogFilename)

raw, err := os.ReadFile(filepath.Join(root, ChangelogFilename))
if err != nil {
if os.IsNotExist(err) {
return
}

errorList.Errorf("Cannot read %s: %s", ChangelogFilename, err)

return
}

var value any
if err := yaml.Unmarshal(raw, &value); err != nil {
errorList.Warnf("invalid YAML in %s:\n%s", ChangelogFilename, err)
}
}
Loading
Loading