Skip to content

Commit

Permalink
refactor: make lint use more accessible data type (#2660)
Browse files Browse the repository at this point in the history
  • Loading branch information
AustinAbro321 committed Jun 28, 2024
2 parents 4c35701 + fe6b2f3 commit 4b00381
Show file tree
Hide file tree
Showing 14 changed files with 628 additions and 422 deletions.
15 changes: 7 additions & 8 deletions src/cmd/dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"github.com/defenseunicorns/zarf/src/config/lang"
"github.com/defenseunicorns/zarf/src/pkg/message"
"github.com/defenseunicorns/zarf/src/pkg/packager"
"github.com/defenseunicorns/zarf/src/pkg/packager/lint"
"github.com/defenseunicorns/zarf/src/pkg/transform"
"github.com/defenseunicorns/zarf/src/pkg/utils"
"github.com/defenseunicorns/zarf/src/types"
Expand Down Expand Up @@ -249,19 +248,19 @@ var devLintCmd = &cobra.Command{
Aliases: []string{"l"},
Short: lang.CmdDevLintShort,
Long: lang.CmdDevLintLong,
Run: func(cmd *cobra.Command, args []string) {
RunE: func(cmd *cobra.Command, args []string) error {
pkgConfig.CreateOpts.BaseDir = common.SetBaseDirectory(args)
v := common.GetViper()
pkgConfig.CreateOpts.SetVariables = helpers.TransformAndMergeMap(
v.GetStringMapString(common.VPkgCreateSet), pkgConfig.CreateOpts.SetVariables, strings.ToUpper)
validator, err := lint.Validate(cmd.Context(), pkgConfig.CreateOpts)

pkgClient, err := packager.New(&pkgConfig)
if err != nil {
message.Fatal(err, err.Error())
}
validator.DisplayFormattedMessage()
if !validator.IsSuccess() {
os.Exit(1)
return err
}
defer pkgClient.ClearTempPaths()

return pkgClient.Lint(cmd.Context())
},
}

Expand Down
2 changes: 1 addition & 1 deletion src/pkg/message/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ func Table(header []string, data [][]string) {
// preventing future characters from taking on the given color
// returns string as normal if color is disabled
func ColorWrap(str string, attr color.Attribute) string {
if config.NoColor {
if config.NoColor || str == "" {
return str
}
return fmt.Sprintf("\x1b[%dm%s\x1b[0m", attr, str)
Expand Down
71 changes: 71 additions & 0 deletions src/pkg/packager/dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ package packager

import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"runtime"

"github.com/defenseunicorns/pkg/helpers/v2"
Expand All @@ -16,7 +18,10 @@ import (
"github.com/defenseunicorns/zarf/src/pkg/message"
"github.com/defenseunicorns/zarf/src/pkg/packager/creator"
"github.com/defenseunicorns/zarf/src/pkg/packager/filters"
"github.com/defenseunicorns/zarf/src/pkg/packager/lint"
"github.com/defenseunicorns/zarf/src/pkg/utils"
"github.com/defenseunicorns/zarf/src/types"
"github.com/fatih/color"
)

// DevDeploy creates + deploys a package in one shot
Expand Down Expand Up @@ -105,3 +110,69 @@ func (p *Packager) DevDeploy(ctx context.Context) error {
// cd back
return os.Chdir(cwd)
}

// Lint ensures a package is valid & follows suggested conventions
func (p *Packager) Lint(ctx context.Context) error {
if err := os.Chdir(p.cfg.CreateOpts.BaseDir); err != nil {
return fmt.Errorf("unable to access directory %q: %w", p.cfg.CreateOpts.BaseDir, err)
}

if err := utils.ReadYaml(layout.ZarfYAML, &p.cfg.Pkg); err != nil {
return err
}

findings, err := lint.Validate(ctx, p.cfg.Pkg, p.cfg.CreateOpts)
if err != nil {
return fmt.Errorf("linting failed: %w", err)
}

if len(findings) == 0 {
message.Successf("0 findings for %q", p.cfg.Pkg.Metadata.Name)
return nil
}

mapOfFindingsByPath := lint.GroupFindingsByPath(findings, types.SevWarn, p.cfg.Pkg.Metadata.Name)

header := []string{"Type", "Path", "Message"}

for _, findings := range mapOfFindingsByPath {
lintData := [][]string{}
for _, finding := range findings {
lintData = append(lintData, []string{
colorWrapSev(finding.Severity),
message.ColorWrap(finding.YqPath, color.FgCyan),
itemizedDescription(finding.Description, finding.Item),
})
}
var packagePathFromUser string
if helpers.IsOCIURL(findings[0].PackagePathOverride) {
packagePathFromUser = findings[0].PackagePathOverride
} else {
packagePathFromUser = filepath.Join(p.cfg.CreateOpts.BaseDir, findings[0].PackagePathOverride)
}
message.Notef("Linting package %q at %s", findings[0].PackageNameOverride, packagePathFromUser)
message.Table(header, lintData)
}

if lint.HasSeverity(findings, types.SevErr) {
return errors.New("errors during lint")
}

return nil
}

func itemizedDescription(description string, item string) string {
if item == "" {
return description
}
return fmt.Sprintf("%s - %s", description, item)
}

func colorWrapSev(s types.Severity) string {
if s == types.SevErr {
return message.ColorWrap("Error", color.FgRed)
} else if s == types.SevWarn {
return message.ColorWrap("Warning", color.FgYellow)
}
return "unknown"
}
41 changes: 41 additions & 0 deletions src/pkg/packager/lint/findings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2021-Present The Zarf Authors

// Package lint contains functions for verifying zarf yaml files are valid
package lint

import (
"github.com/defenseunicorns/pkg/helpers/v2"
"github.com/defenseunicorns/zarf/src/types"
)

// GroupFindingsByPath groups findings by their package path
func GroupFindingsByPath(findings []types.PackageFinding, severity types.Severity, packageName string) map[string][]types.PackageFinding {
findings = helpers.RemoveMatches(findings, func(finding types.PackageFinding) bool {
return finding.Severity > severity
})
for i := range findings {
if findings[i].PackageNameOverride == "" {
findings[i].PackageNameOverride = packageName
}
if findings[i].PackagePathOverride == "" {
findings[i].PackagePathOverride = "."
}
}

mapOfFindingsByPath := make(map[string][]types.PackageFinding)
for _, finding := range findings {
mapOfFindingsByPath[finding.PackagePathOverride] = append(mapOfFindingsByPath[finding.PackagePathOverride], finding)
}
return mapOfFindingsByPath
}

// HasSeverity returns true if the findings contain a severity equal to or greater than the given severity
func HasSeverity(findings []types.PackageFinding, severity types.Severity) bool {
for _, finding := range findings {
if finding.Severity <= severity {
return true
}
}
return false
}
122 changes: 122 additions & 0 deletions src/pkg/packager/lint/findings_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2021-Present The Zarf Authors

// Package lint contains functions for verifying zarf yaml files are valid
package lint

import (
"testing"

"github.com/defenseunicorns/zarf/src/types"
"github.com/stretchr/testify/require"
)

func TestGroupFindingsByPath(t *testing.T) {
t.Parallel()
tests := []struct {
name string
findings []types.PackageFinding
severity types.Severity
packageName string
want map[string][]types.PackageFinding
}{
{
name: "same package multiple findings",
findings: []types.PackageFinding{
{Severity: types.SevWarn, PackageNameOverride: "import", PackagePathOverride: "path"},
{Severity: types.SevWarn, PackageNameOverride: "import", PackagePathOverride: "path"},
},
severity: types.SevWarn,
packageName: "testPackage",
want: map[string][]types.PackageFinding{
"path": {
{Severity: types.SevWarn, PackageNameOverride: "import", PackagePathOverride: "path"},
{Severity: types.SevWarn, PackageNameOverride: "import", PackagePathOverride: "path"},
},
},
},
{
name: "different packages single finding",
findings: []types.PackageFinding{
{Severity: types.SevWarn, PackageNameOverride: "import", PackagePathOverride: "path"},
{Severity: types.SevErr, PackageNameOverride: "", PackagePathOverride: ""},
},
severity: types.SevWarn,
packageName: "testPackage",
want: map[string][]types.PackageFinding{
"path": {{Severity: types.SevWarn, PackageNameOverride: "import", PackagePathOverride: "path"}},
".": {{Severity: types.SevErr, PackageNameOverride: "testPackage", PackagePathOverride: "."}},
},
},
{
name: "Multiple findings, mixed severity",
findings: []types.PackageFinding{
{Severity: types.SevWarn, PackageNameOverride: "", PackagePathOverride: ""},
{Severity: types.SevErr, PackageNameOverride: "", PackagePathOverride: ""},
},
severity: types.SevErr,
packageName: "testPackage",
want: map[string][]types.PackageFinding{
".": {{Severity: types.SevErr, PackageNameOverride: "testPackage", PackagePathOverride: "."}},
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
require.Equal(t, tt.want, GroupFindingsByPath(tt.findings, tt.severity, tt.packageName))
})
}
}

func TestHasSeverity(t *testing.T) {
t.Parallel()
tests := []struct {
name string
severity types.Severity
expected bool
findings []types.PackageFinding
}{
{
name: "error severity present",
findings: []types.PackageFinding{
{
Severity: types.SevErr,
},
},
severity: types.SevErr,
expected: true,
},
{
name: "error severity not present",
findings: []types.PackageFinding{
{
Severity: types.SevWarn,
},
},
severity: types.SevErr,
expected: false,
},
{
name: "err and warning severity present",
findings: []types.PackageFinding{
{
Severity: types.SevWarn,
},
{
Severity: types.SevErr,
},
},
severity: types.SevErr,
expected: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
require.Equal(t, tt.expected, HasSeverity(tt.findings, tt.severity))
})
}
}
Loading

0 comments on commit 4b00381

Please sign in to comment.