OpenPeeDeeP / depguard

Go linter that checks if package imports are in a list of acceptable packages.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Reusing configuration

ianlewis opened this issue · comments

Is there a way to reuse configuration from one rule in another? For example, I'd like to create a common rule for all code, and then sub-rules for test and non-test code. As it stands currently, I think I need to largely duplicate the rule for test and non-test code.

rules:
  main:
    files:
      - $all
      - "!$test"
    allow:
      # allowed packages
    deny:
      # denied packages
  test:
    files:
      - $test
    allow:
      # Copy every everything from main here.
      "github.com/google/go-cmp"
    deny:
      # 

Also, if I need exceptions for a particular package I can't just specify the exception, I have to exclude the package files from the test and non-test rules, copy both the test and non-test rules and then update them with the exception. This becomes pretty hard to maintain pretty quickly.

For example, all of the following rules would be mostly the same except for some small differences.

rules:
  exception:
    # rules for exception
  exception-tests:
    # rules for tests for the exceptional package
  main:
     # rules for everything else
  test:
     # rules for tests for everything else

Don't see the need to copy configs. A file can match multiple rules
https://github.com/OpenPeeDeeP/depguard/blob/6a315f2326b2084d60c0a056bcafa10d1e9f5bd7/settings_test.go#LL602C1-L606C4

rules:
  global:
    files:
      - $all
    allow:
      # allowed packages
    deny:
      # denied packages
  test:
    files:
      - $test
    allow:
      "github.com/google/go-cmp"
    deny:
      # 

Removing the "!$test" will from main (I renamed main to global above) will allow all test files to match on both lists and should lint appropriately.

Hmm, I don't see it doing that. When I used a configuration like this all of my test files failed with errors saying that I wasn't allowed to import stdlib packages. So it seemed like all rules were run completely independently of each other (I'm using golangci-lint 1.53.2).

I may have been doing something wrong but I'll try to add a simple example when my hands free up.

I do see the flaw in the code. It does match multiple list, but each list is checked independently of each other, so file has to pass all rule sets they are in... I'll see if I can plan something up, will probably use the $variables I introduced in this version as that is the best backwards compatible way to do it. Brute force would be to combine the lists but I believe that is backwards breaking behavior...

https://github.com/OpenPeeDeeP/depguard/blob/6a315f2326b2084d60c0a056bcafa10d1e9f5bd7/depguard.go#LL72C3-L88C4

Relevant code to above comment... Notice that it checks if it is allowed in each list, and if it fails throws a lint error.

Apologies. I must have just done something very wrong. I was able to get it to work the way you described and I now can't recreate the config that was failing for me.

I think that what i was trying to do initially was to create a blanket deny in global but then allow it in test when what I should have done is leave it out of the global allow list and only add it to test. Another reason for my confusion is that there is a global allow rule for github.com/google org in my case.

Ok, If I have something like the following all the files under exception/ fail badly for me.

rules:                                                                                                                                                                                                                                                                                                                                                                     
  global:                                                                                                                                                                                                                                                                                                                                                                     
    files:                                                                                                                                                                                                                                                                                                                                                                 
      - $all                                                                                                                                                                                                                                                                                                                                                               
    allow:                                                                                                                                                                                                                                                                                                                                                                 
      - $gostd                                                                                                                                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                                                                                                                           
  exception:                                                                                                                                                                                                                                                                                                                                                               
    files:                                                                                                                                                                                                                                                                                                                                                                 
      - "**/exception/**/*.go"                                                                                                                                                                                                                                                                                                                                               
    allow:                                                                                                                                                                                                                                                                                                                                                                 
      - "github.com/google/go-cmp"

So it seems that if the allow was empty in exception it will allow anything and so it effectively merges well with global.allow but when I specify a exception.allow then only that list is strictly allowed.

i.e. I can do this.

rules:                                                                                                                                                                                                                                                                                                                                                                     
  all:                                                                                                                                                                                                                                                                                                                                                                     
    files:                                                                                                                                                                                                                                                                                                                                                                 
      - $all                                                                                                                                                                                                                                                                                                                                                               
    allow:                                                                                                                                                                                                                                                                                                                                                                 
      - $gostd                                                                                                                                                                                                                                                                                                                                                             
      - "github.com/google/go-cmp"                                                                                                                                                                                                                                                                                                                                         
                                                                                                                                                                                                                                                                                                                                                                           
  main:                                                                                                                                                                                                                                                                                                                                                                    
    files:                                                                                                                                                                                                                                                                                                                                                                 
      - $all                                                                                                                                                                                                                                                                                                                                                               
      - "!**/exception/**/*.go"                                                                                                                                                                                                                                                                                                                                              
    deny:                                                                                                                                                                                                                                                                                                                                                                  
      - pkg: "github.com/google/go-cmp"                                                                                                                                                                                                                                                                                                                                    
        desc: Please don't use go-cmp

So If I want to have a global allow list and grant exceptions for individual directories it seems the best way to do that currently is to allow everything in the global rule and deny it in the main rule. This is kind of the reverse of how I wanted to express the exception.

commented

I'm kinda running into the same issue. I think this linter has great potential but I just couldn't get the lists to "overlap" to prevent having to specify dependencies twice. I personally think the README needs a big section that explains the "mental model" a bit better. Maybe more examples, or maybe just different approaches to whitelisting/blacklisting in certain ways. Like, what happens if a package is in two lists? what if it is allowed in one and denied in the other? Is the order of rules important? etc.

If it helps, I'm trying it on this repo: https://github.com/crewlinker/clgo with a end configuration like this:

  depguard:
    rules:
      tasks:
        files:
          - "**/magefiles/*.go"
        allow:
          - $gostd
          - github.com/magefile/mage
      main:
        files:
          - $all
          - "!$test"
          - "!**/magefiles/*.go"
          - "!**/clzap/logs_testutil.go" #special exception for ginkgo writer
        allow:
          - $gostd
          - github.com/aws/aws-lambda-go/lambda
          - github.com/crewlinker/clgo/
          - github.com/caarlos0/env/v6
          - github.com/aws/aws-sdk-go-v2
          - github.com/go-logr/zapr
          - github.com/jackc/pgx/v5
          - github.com/redis/go-redis
          - github.com/XSAM/otelsql
          - ariga.io/atlas/sql
      zaplog:
        files:
          - "**/clzap/logs_testutil.go"
        allow:
          - $gostd
          - github.com/onsi/ginkgo/v2
      tests:
        files:
          - $test
        allow: 
          - $gostd
          - github.com/crewlinker/clgo/
          - github.com/onsi/ginkgo/v2
          - github.com/joho/godotenv
          - github.com/caarlos0/env/v6
          - github.com/aws/aws-sdk-go-v2
          - github.com/go-logr/zapr
          - github.com/jackc/pgx/v5
          - github.com/redis/go-redis
          - github.com/XSAM/otelsql
          - ariga.io/atlas/sql
          - github.com/onsi/gomega

I would expect that if I remove !$test from the "main" rule it would starting to overlap and I wouldn't have to provide the same dependencies in the "tests" rule but it didn't work for me.

NOTE2: In the same repo above I didn't need to specify go.uber.org/fx or go.uber.org/zap at all, which confused me as well. I feel like I'm just not getting it.

Yeah, IIUC, you'd need to do something like the following if you want to dedup the allow list. Which is a bit ununintuitive to express.

depguard:
  rules:
    global:
      files:
        - $all
        - "!**/magefiles/*.go"
        - "!**/clzap/logs_testutil.go"
      allow:
        - $gostd
        - github.com/aws/aws-sdk-go-v2
        - github.com/crewlinker/clgo/
        - github.com/caarlos0/env/v6
        - github.com/go-logr/zapr
        - github.com/jackc/pgx/v5
        - github.com/redis/go-redis
        - github.com/XSAM/otelsql
        - ariga.io/atlas/sql

        # Allowed in non-test code
        - github.com/aws/aws-lambda-go/lambda

        # Allowed in test code.
        - github.com/onsi/ginkgo/v2
        - github.com/joho/godotenv
        - github.com/onsi/gomega

    tasks:
      files:
        - "**/magefiles/*.go"
      allow:
        - $gostd
        - github.com/magefile/mage

    zaplog:
      files:
        - "**/clzap/logs_testutil.go"
      allow:
        - $gostd
        - github.com/onsi/ginkgo/v2

    main:
      files:
        - $all
        - "!$test"
        - "!**/magefiles/*.go" # special exception for magefiles
        - "!**/clzap/logs_testutil.go" # special exception for ginkgo writer
      deny:
        - pkg: github.com/onsi/ginkgo/v2
          description: Don't use.
        - pkg: github.com/joho/godotenv
          description: Don't use.
        - pkg: github.com/onsi/gomega
          description: Don't use.

    tests:
      files:
        - $test
      deny:
        - pkg: github.com/aws/aws-lambda-go/lambda
          description: Don't use.

I have not forgotten about this, just have not had the time to look for, much less develop, a backwards compatible solution.

Fixing the loop I linked earlier is a breaking change as configurations that were setup to use it, were expecting that behavior... The loop assumes it needs to pass all lists a file matches for (which is most strict AND logic) but appears some want a less strict OR logic. Maybe add a configuration that defaults to the AND logic but can be configure to OR logic...

It seems 2 options are backwards compatible.

  1. Add variables that allow you to reference another list's allow/deny lists
  2. Create a configuration field that defaults to the "STRICT" mode but can be configured to a "LAX" mode (naming should be revised).

Open to other solutions or some pro/con conversation on either solution? In theory we could introduce both options.

So I have Strict and Lax mode for the package matching setup. But I would like to add reference variables as well to cover this issue more thoroughly.