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
56 changes: 33 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,31 +87,41 @@ Patterner uses a `.patterner.yml` file for configuration. The configuration incl
```yaml
workspaceID: xxxxxxxXXxxxxxxxxxxxxx
lint:
pipeline:
deprecatedFeature:
enabled: true
allowCELScript: false
allowDraft: false
allowStateFlow: false
insecureAuthorization:
enabled: false
stepLength:
enabled: true
max: 30
multipleMutations:
enabled: true
queryBeforeMutation:
enabled: true
tailordb:
deprecatedFeature:
enabled: true
allowDraft: false
allowCELHooks: false
stateflow:
deprecatedFeature:
enabled: true
acceptable: 5
rules:
pipeline:
deprecatedFeature:
enabled: true
allowCELScript: false
allowDraft: false
allowStateFlow: false
insecureAuthorization:
enabled: false
stepLength:
enabled: true
max: 30
multipleMutations:
enabled: true
queryBeforeMutation:
enabled: true
tailordb:
deprecatedFeature:
enabled: true
allowDraft: false
allowCELHooks: false
stateflow:
deprecatedFeature:
enabled: true
```

### Lint Configuration

#### Acceptable Warnings
- **acceptable** - Set the maximum number of acceptable lint warnings (default: 0)
- When the number of warnings exceeds this value, the lint command will exit with a failure status
- This allows you to gradually improve code quality by setting a reasonable warning threshold
- Example: `acceptable: 5` allows up to 5 warnings before failing

### Lint Rules

#### Pipeline Rules
Expand Down
8 changes: 5 additions & 3 deletions cmd/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ THE SOFTWARE.
package cmd

import (
"errors"
"fmt"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -58,8 +57,11 @@ var lintCmd = &cobra.Command{
for _, w := range warns {
fmt.Printf("[%s] %s: %s\n", w.Type, w.Name, w.Message)
}
if len(warns) > 0 {
return errors.New("lint warnings found")
if len(warns) > cfg.Lint.Acceptable {
if cfg.Lint.Acceptable == 0 {
return fmt.Errorf("%d warnings found", len(warns))
}
return fmt.Errorf("%d warnings found, which exceeds the acceptable number of %d", len(warns), cfg.Lint.Acceptable)
}
return nil
},
Expand Down
5 changes: 5 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ type Config struct {
}

type Lint struct {
Acceptable int `default:"0" yaml:"acceptable,omitempty"`
Rules Rules `yaml:"rules,omitempty,omitzero"`
}

type Rules struct {
Pipeline Pipeline `yaml:"pipeline,omitempty,omitzero"`
TailorDB TailorDB `yaml:"tailordb,omitempty,omitzero"`
StateFlow StateFlow `yaml:"stateflow,omitempty,omitzero"`
Expand Down
64 changes: 33 additions & 31 deletions tailor/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,41 @@ func createTestConfig(t *testing.T) *config.Config {
return &config.Config{
WorkspaceID: "test-workspace-id",
Lint: config.Lint{
TailorDB: config.TailorDB{
DeprecatedFeature: config.TailorDBDeprecatedFeature{
Enabled: true,
AllowDraft: false,
AllowCELHooks: false,
AllowTypePermission: false,
AllowRecordPermission: false,
Rules: config.Rules{
TailorDB: config.TailorDB{
DeprecatedFeature: config.TailorDBDeprecatedFeature{
Enabled: true,
AllowDraft: false,
AllowCELHooks: false,
AllowTypePermission: false,
AllowRecordPermission: false,
},
},
},
Pipeline: config.Pipeline{
InsecureAuthorization: config.InsecureAuthorization{
Enabled: true,
},
StepLength: config.StepLength{
Enabled: true,
Max: 10,
},
DeprecatedFeature: config.PipelineDeprecatedFeature{
Enabled: true,
AllowStateFlow: false,
AllowDraft: false,
AllowCELScript: false,
Pipeline: config.Pipeline{
InsecureAuthorization: config.InsecureAuthorization{
Enabled: true,
},
StepLength: config.StepLength{
Enabled: true,
Max: 10,
},
DeprecatedFeature: config.PipelineDeprecatedFeature{
Enabled: true,
AllowStateFlow: false,
AllowDraft: false,
AllowCELScript: false,
},
MultipleMutations: config.MultipleMutations{
Enabled: true,
},
QueryBeforeMutation: config.QueryBeforeMutation{
Enabled: true,
},
},
MultipleMutations: config.MultipleMutations{
Enabled: true,
},
QueryBeforeMutation: config.QueryBeforeMutation{
Enabled: true,
},
},
StateFlow: config.StateFlow{
DeprecatedFeature: config.StateFlowDeprecatedFeature{
Enabled: true,
StateFlow: config.StateFlow{
DeprecatedFeature: config.StateFlowDeprecatedFeature{
Enabled: true,
},
},
},
},
Expand Down
32 changes: 16 additions & 16 deletions tailor/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,22 @@ func (c *Client) Lint(resources *Resources) ([]*LintWarn, error) {
for _, t := range db.Types {
typeNames = append(typeNames, t.Name)

if c.cfg.Lint.TailorDB.DeprecatedFeature.Enabled {
if !c.cfg.Lint.TailorDB.DeprecatedFeature.AllowDraft && t.Draft {
if c.cfg.Lint.Rules.TailorDB.DeprecatedFeature.Enabled {
if !c.cfg.Lint.Rules.TailorDB.DeprecatedFeature.AllowDraft && t.Draft {
warns = append(warns, &LintWarn{
Type: LintTargetTypeTailorDB,
Name: fmt.Sprintf("%s/%s", db.NamespaceName, t.Name),
Message: "Draft feature is deprecated",
})
}
if !c.cfg.Lint.TailorDB.DeprecatedFeature.AllowTypePermission && t.TypePermission != nil {
if !c.cfg.Lint.Rules.TailorDB.DeprecatedFeature.AllowTypePermission && t.TypePermission != nil {
warns = append(warns, &LintWarn{
Type: LintTargetTypeTailorDB,
Name: fmt.Sprintf("%s/%s", db.NamespaceName, t.Name),
Message: "Type-level permission is deprecated. Use `Permission` or `GQLPermission` instead",
})
}
if !c.cfg.Lint.TailorDB.DeprecatedFeature.AllowRecordPermission && t.RecordPermission != nil {
if !c.cfg.Lint.Rules.TailorDB.DeprecatedFeature.AllowRecordPermission && t.RecordPermission != nil {
warns = append(warns, &LintWarn{
Type: LintTargetTypeTailorDB,
Name: fmt.Sprintf("%s/%s", db.NamespaceName, t.Name),
Expand All @@ -60,7 +60,7 @@ func (c *Client) Lint(resources *Resources) ([]*LintWarn, error) {
}
}

if c.cfg.Lint.TailorDB.DeprecatedFeature.Enabled && !c.cfg.Lint.TailorDB.DeprecatedFeature.AllowCELHooks {
if c.cfg.Lint.Rules.TailorDB.DeprecatedFeature.Enabled && !c.cfg.Lint.Rules.TailorDB.DeprecatedFeature.AllowCELHooks {
for _, f := range t.Fields {
if f.Hooks.CreateExpr != "" || f.Hooks.UpdateExpr != "" {
warns = append(warns, &LintWarn{
Expand All @@ -78,7 +78,7 @@ func (c *Client) Lint(resources *Resources) ([]*LintWarn, error) {
for _, p := range resources.Pipelines {
for _, r := range p.Resolvers {
// Pipeline/InsecureAuthorization
if c.cfg.Lint.Pipeline.InsecureAuthorization.Enabled && (r.Authorization == "true" || r.Authorization == "true==true") {
if c.cfg.Lint.Rules.Pipeline.InsecureAuthorization.Enabled && (r.Authorization == "true" || r.Authorization == "true==true") {
warns = append(warns, &LintWarn{
Type: LintTargetTypePipeline,
Name: fmt.Sprintf("%s/%s", p.NamespaceName, r.Name),
Expand All @@ -88,11 +88,11 @@ func (c *Client) Lint(resources *Resources) ([]*LintWarn, error) {

stepLength := len(r.Steps)
// Pipeline/StepLength
if c.cfg.Lint.Pipeline.StepLength.Enabled && stepLength > c.cfg.Lint.Pipeline.StepLength.Max {
if c.cfg.Lint.Rules.Pipeline.StepLength.Enabled && stepLength > c.cfg.Lint.Rules.Pipeline.StepLength.Max {
warns = append(warns, &LintWarn{
Type: LintTargetTypePipeline,
Name: fmt.Sprintf("%s/%s", p.NamespaceName, r.Name),
Message: fmt.Sprintf("resolver has too many steps (%d > %d)", stepLength, c.cfg.Lint.Pipeline.StepLength.Max),
Message: fmt.Sprintf("resolver has too many steps (%d > %d)", stepLength, c.cfg.Lint.Rules.Pipeline.StepLength.Max),
})
}

Expand All @@ -110,9 +110,9 @@ func (c *Client) Lint(resources *Resources) ([]*LintWarn, error) {
for _, selection := range op.SelectionSet {
switch sel := selection.(type) {
case *ast.Field:
if c.cfg.Lint.Pipeline.DeprecatedFeature.Enabled {
if c.cfg.Lint.Rules.Pipeline.DeprecatedFeature.Enabled {
// StateFlow
if !c.cfg.Lint.Pipeline.DeprecatedFeature.AllowStateFlow {
if !c.cfg.Lint.Rules.Pipeline.DeprecatedFeature.AllowStateFlow {
if slices.Contains(stateFlowMutations, sel.Name) {
warns = append(warns, &LintWarn{
Type: LintTargetTypePipeline,
Expand All @@ -123,7 +123,7 @@ func (c *Client) Lint(resources *Resources) ([]*LintWarn, error) {
}

// Draft
if !c.cfg.Lint.Pipeline.DeprecatedFeature.AllowDraft {
if !c.cfg.Lint.Rules.Pipeline.DeprecatedFeature.AllowDraft {
if replaced := draftMutationPrefixRe.ReplaceAllString(sel.Name, ""); replaced != sel.Name {
if slices.Contains(typeNames, replaced) {
warns = append(warns, &LintWarn{
Expand All @@ -139,7 +139,7 @@ func (c *Client) Lint(resources *Resources) ([]*LintWarn, error) {
}
}
}
if c.cfg.Lint.Pipeline.DeprecatedFeature.Enabled && !c.cfg.Lint.Pipeline.DeprecatedFeature.AllowCELScript {
if c.cfg.Lint.Rules.Pipeline.DeprecatedFeature.Enabled && !c.cfg.Lint.Rules.Pipeline.DeprecatedFeature.AllowCELScript {
if s.PreValidation != "" {
warns = append(warns, &LintWarn{
Type: LintTargetTypePipeline,
Expand Down Expand Up @@ -170,7 +170,7 @@ func (c *Client) Lint(resources *Resources) ([]*LintWarn, error) {
}
}
}
if c.cfg.Lint.Pipeline.MultipleMutations.Enabled {
if c.cfg.Lint.Rules.Pipeline.MultipleMutations.Enabled {
var count int
for _, op := range operations {
if op == "mutation" {
Expand All @@ -185,7 +185,7 @@ func (c *Client) Lint(resources *Resources) ([]*LintWarn, error) {
})
}
}
if c.cfg.Lint.Pipeline.QueryBeforeMutation.Enabled {
if c.cfg.Lint.Rules.Pipeline.QueryBeforeMutation.Enabled {
if slices.Contains(operations, "mutation") && slices.Contains(operations, "query") && slices.Index(operations, "mutation") > slices.Index(operations, "query") {
warns = append(warns, &LintWarn{
Type: LintTargetTypePipeline,
Expand All @@ -198,9 +198,9 @@ func (c *Client) Lint(resources *Resources) ([]*LintWarn, error) {
}

// StateFlow Linting
if c.cfg.Lint.StateFlow.DeprecatedFeature.Enabled {
if c.cfg.Lint.Rules.StateFlow.DeprecatedFeature.Enabled {
for _, sf := range resources.StateFlows {
if !c.cfg.Lint.StateFlow.DeprecatedFeature.Enabled {
if !c.cfg.Lint.Rules.StateFlow.DeprecatedFeature.Enabled {
continue
}
warns = append(warns, &LintWarn{
Expand Down
Loading