diff --git a/pkg/lint/rules.go b/pkg/lint/rules.go index a00321fa5..c1dc190f4 100644 --- a/pkg/lint/rules.go +++ b/pkg/lint/rules.go @@ -468,6 +468,94 @@ var AllRules = func(l *Linter) Rules { //nolint:gocyclo return err }, }, + { + Name: "valid-version-stream", + Description: "check the provides and tag filter if version stream suffix is passed to package name", + Severity: SeverityWarning, + LintFunc: func(c config.Configuration) error { + // This regex captures a string that ends with a semantic version (semver) suffix. + // It returns two groups: + // - Group 1: The prefix part of the string before the semver. + // - Group 2: The semver (major.minor or major.minor.patch). + // If there is no semver suffix, the string will not match. + re := regexp.MustCompile(`^(.*?)-(\d+\.\d+(?:\.\d+)?)$`) + + // It means that the package is not a version streamed, so early return. + if !re.MatchString(c.Package.Name) { + return nil + } + + matches := re.FindStringSubmatch(c.Package.Name) + if len(matches) != 3 { + return fmt.Errorf("invalid package name %s for version stream, regex matches %+v", c.Package.Name, matches) + } + + packageName := matches[1] + versionStream := matches[2] + + if !strings.HasPrefix(c.Package.Version, versionStream) { + return fmt.Errorf("package is version streamed but package.version %s starts with different than given version stream %s", c.Package.Version, versionStream) + } + + var providesList []string + addToProvidesList := func(key string) { + providesList = append(providesList, fmt.Sprintf("%s=%s-r%d", key, c.Package.Version, c.Package.Epoch)) + providesList = append(providesList, fmt.Sprintf("%s=%s-r%d", key, versionStream, c.Package.Epoch)) + providesList = append(providesList, fmt.Sprintf("%s=%s", key, c.Package.Version)) + providesList = append(providesList, fmt.Sprintf("%s=%s", key, versionStream)) + providesList = append(providesList, fmt.Sprintf("%s=%s.999", key, versionStream)) + } + addToProvidesList(packageName) + + // Some packages have different provides for package name, i.e. python-3 instead of python. + if majorMinor := strings.Split(versionStream, "."); len(majorMinor) > 1 { + addToProvidesList(fmt.Sprintf("%s-%s", packageName, majorMinor[0])) + } + + anyMatch := false + for _, provides := range providesList { + if slices.Contains(c.Package.Dependencies.Provides, provides) { + anyMatch = true + break + } + } + + if !anyMatch { + return fmt.Errorf("package is version streamed but %s=${{package.full-version}} is missing on dependencies.provides", packageName) + } + + // Any subpackage that contains the package name, should follow `${{package.name}}-xyz` format. + for _, s := range c.Subpackages { + if strings.Contains(s.Name, packageName) { + if !strings.HasPrefix(s.Name, c.Package.Name) { + return fmt.Errorf("subpackage %s should be in format ${{package.name}}-XYZ-SUBPACKAGENAME for a valid version stream", s.Name) + } + } + } + + if c.Update.Enabled && !c.Update.Manual && c.Update.GitHubMonitor != nil { + prefixesToCheck := []string{"", "v", packageName, "release", strings.ReplaceAll(packageName, "-fips", ""), c.Update.GitHubMonitor.StripPrefix} + separators := []string{"", ".", "-", "_"} + versionsToCheck := []string{versionStream, strings.ReplaceAll(versionStream, ".", "-"), strings.ReplaceAll(versionStream, ".", "_")} + + var filtersToCheck []string + for _, prefix := range prefixesToCheck { + for _, separator := range separators { + for _, version := range versionsToCheck { + for _, suffix := range separators { + filtersToCheck = append(filtersToCheck, prefix+separator+version+suffix) + } + } + } + } + + if !slices.Contains(filtersToCheck, c.Update.GitHubMonitor.TagFilter) && !slices.Contains(filtersToCheck, c.Update.GitHubMonitor.TagFilterPrefix) { + return fmt.Errorf("package is version streamed but tag filter %s is mismatch on update.github", versionStream) + } + } + return nil + }, + }, } } diff --git a/pkg/lint/rules_test.go b/pkg/lint/rules_test.go index bca989efe..698cd407d 100644 --- a/pkg/lint/rules_test.go +++ b/pkg/lint/rules_test.go @@ -415,6 +415,78 @@ func TestLinter_Rules(t *testing.T) { wantErr: false, matches: 1, }, + { + file: "version-stream-missing-provides-1.2.yaml", + minSeverity: SeverityWarning, + want: EvalResult{ + File: "version-stream-missing-provides-1.2", + Errors: EvalRuleErrors{ + { + Rule: Rule{ + Name: "valid-version-stream", + Severity: SeverityWarning, + }, + Error: fmt.Errorf("[valid-version-stream]: package is version streamed but version-stream-missing-provides=${{package.full-version}} is missing on dependencies.provides (WARNING)"), + }, + }, + }, + wantErr: false, + matches: 1, + }, + { + file: "version-stream-missing-update-tagfilter-1.2.yaml", + minSeverity: SeverityWarning, + want: EvalResult{ + File: "version-stream-missing-update-tagfilter-1.2", + Errors: EvalRuleErrors{ + { + Rule: Rule{ + Name: "valid-version-stream", + Severity: SeverityWarning, + }, + Error: fmt.Errorf("[valid-version-stream]: package is version streamed but tag filter 1.2 is mismatch on update.github (WARNING)"), + }, + }, + }, + wantErr: false, + matches: 1, + }, + { + file: "version-stream-missing-update-tagfilter-1.2-999.yaml", + minSeverity: SeverityWarning, + want: EvalResult{ + File: "version-stream-missing-update-tagfilter-1.2", + Errors: EvalRuleErrors{ + { + Rule: Rule{ + Name: "valid-version-stream", + Severity: SeverityWarning, + }, + Error: fmt.Errorf("[valid-version-stream]: package is version streamed but tag filter 1.2 is mismatch on update.github (WARNING)"), + }, + }, + }, + wantErr: false, + matches: 1, + }, + { + file: "version-stream-wrong-subpackage-naming-1.2.yaml", + minSeverity: SeverityWarning, + want: EvalResult{ + File: "version-stream-wrong-subpackage-naming-1.2", + Errors: EvalRuleErrors{ + { + Rule: Rule{ + Name: "valid-version-stream", + Severity: SeverityWarning, + }, + Error: fmt.Errorf("[valid-version-stream]: package is version streamed but version-stream-wrong-subpackage-naming=${{package.full-version}} is missing on dependencies.provides (WARNING)"), + }, + }, + }, + wantErr: false, + matches: 1, + }, { file: "valid-update-schedule.yaml", minSeverity: SeverityWarning, @@ -433,6 +505,24 @@ func TestLinter_Rules(t *testing.T) { wantErr: false, matches: 1, }, + { + file: "version-stream-mismatch-version-1.2.yaml", + minSeverity: SeverityWarning, + want: EvalResult{ + File: "version-stream-mismatch-version-1.2", + Errors: EvalRuleErrors{ + { + Rule: Rule{ + Name: "valid-version-stream", + Severity: SeverityWarning, + }, + Error: fmt.Errorf("[valid-version-stream]: package is version streamed but package.version 1.7 starts with different than given version stream 1.2 (WARNING)"), + }, + }, + }, + wantErr: false, + matches: 1, + }, } for _, tt := range tests { diff --git a/pkg/lint/testdata/files/version-stream-mismatch-version-1.2.yaml b/pkg/lint/testdata/files/version-stream-mismatch-version-1.2.yaml new file mode 100644 index 000000000..3a495d13b --- /dev/null +++ b/pkg/lint/testdata/files/version-stream-mismatch-version-1.2.yaml @@ -0,0 +1,18 @@ +package: + name: version-stream-mismatch-version-1.2 + version: 1.7 # Supposed to be 1.2.* + epoch: 0 + description: "a version-streamed package with no dependencies.provides" + +pipeline: + - uses: fetch + with: + uri: https://test.com/version-stream-mismatch-version/${{package.version}}.tar.gz + expected-sha256: ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269 + +test: + pipeline: + - runs: "echo 'test'" + +update: + enabled: true diff --git a/pkg/lint/testdata/files/version-stream-missing-provides-1.2.yaml b/pkg/lint/testdata/files/version-stream-missing-provides-1.2.yaml new file mode 100644 index 000000000..31c1a5f4b --- /dev/null +++ b/pkg/lint/testdata/files/version-stream-missing-provides-1.2.yaml @@ -0,0 +1,18 @@ +package: + name: version-stream-missing-provides-1.2 + version: 1.2.3 + epoch: 0 + description: "a version-streamed package with no dependencies.provides" + +pipeline: + - uses: fetch + with: + uri: https://test.com/version-stream-missing-provides/${{package.version}}.tar.gz + expected-sha256: ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269 + +test: + pipeline: + - runs: "echo 'test'" + +update: + enabled: true diff --git a/pkg/lint/testdata/files/version-stream-missing-update-tagfilter-1.2-999.yaml b/pkg/lint/testdata/files/version-stream-missing-update-tagfilter-1.2-999.yaml new file mode 100644 index 000000000..da0f4c620 --- /dev/null +++ b/pkg/lint/testdata/files/version-stream-missing-update-tagfilter-1.2-999.yaml @@ -0,0 +1,24 @@ +package: + name: version-stream-missing-update-tagfilter-1.2 + version: 1.2.3 + epoch: 0 + description: "a version-streamed package with no dependencies.provides" + dependencies: + provides: + - version-stream-missing-update-tagfilter=1.2.999 + +pipeline: + - uses: fetch + with: + uri: https://test.com/version-stream-missing-update-tagfilter/${{package.version}}.tar.gz + expected-sha256: ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269 + +test: + pipeline: + - runs: "echo 'test'" + +update: + enabled: true + github: + identifier: test/version-stream-missing-update-tagfilter + use-tag: true diff --git a/pkg/lint/testdata/files/version-stream-missing-update-tagfilter-1.2.yaml b/pkg/lint/testdata/files/version-stream-missing-update-tagfilter-1.2.yaml new file mode 100644 index 000000000..198efe940 --- /dev/null +++ b/pkg/lint/testdata/files/version-stream-missing-update-tagfilter-1.2.yaml @@ -0,0 +1,24 @@ +package: + name: version-stream-missing-update-tagfilter-1.2 + version: 1.2.3 + epoch: 0 + description: "a version-streamed package with no dependencies.provides" + dependencies: + provides: + - version-stream-missing-update-tagfilter=${{package.full-version}} + +pipeline: + - uses: fetch + with: + uri: https://test.com/version-stream-missing-update-tagfilter/${{package.version}}.tar.gz + expected-sha256: ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269 + +test: + pipeline: + - runs: "echo 'test'" + +update: + enabled: true + github: + identifier: test/version-stream-missing-update-tagfilter + use-tag: true diff --git a/pkg/lint/testdata/files/version-stream-wrong-subpackage-naming-1.2.yaml b/pkg/lint/testdata/files/version-stream-wrong-subpackage-naming-1.2.yaml new file mode 100644 index 000000000..389acecbb --- /dev/null +++ b/pkg/lint/testdata/files/version-stream-wrong-subpackage-naming-1.2.yaml @@ -0,0 +1,28 @@ +package: + name: version-stream-wrong-subpackage-naming-1.2 + version: 1.2.3 + epoch: 0 + description: "a version-streamed package with no dependencies.provides" + +pipeline: + - uses: fetch + with: + uri: https://test.com/version-stream-missing-provides/${{package.version}}.tar.gz + expected-sha256: ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269 + +subpackages: + - name: this-is-subpackage-of-version-stream-wrong-subpackage-naming-1.2 + description: "a package with incorrect subpackage prefix" + pipeline: + - runs: exit 0 + - name: subpackage-of-version-stream-wrong-subpackage-naming-foo-bar-baz-1.2 + description: "a package with incorrect subpackage prefix" + pipeline: + - runs: exit 0 + +test: + pipeline: + - runs: "echo 'test'" + +update: + enabled: true