Skip to content

Commit

Permalink
Merge pull request #39 from g4s8/linter
Browse files Browse the repository at this point in the history
Add env linter
  • Loading branch information
g4s8 authored Nov 19, 2024
2 parents 3492691 + 05be2a5 commit 44b51d9
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
restore-keys: |
${{ runner.os }}-go-
- name: Build
run: go build -v
run: go build -v ./...
- name: Test
run: go test -v
- name: Check examples
Expand Down
1 change: 1 addition & 0 deletions docenv/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/docenv
34 changes: 34 additions & 0 deletions docenv/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# docenv - linter for environment documentation

The linter check that all environment variable fields with `env` tag are documented.

## Example

The struct with undocumented fields:
```go
type Config struct {
Hosts []string `env:"HOST,required", envSeparator:";"`
Port int `env:"PORT,notEmpty"`
}
```

Run the linter:
```bash
$ go vet -vettool=docenv ./config.go
config.go:12:2: field `Hosts` with `env` tag should have a documentation comment
config.go:13:2: field `Port` with `env` tag should have a documentation comment
```

## Usage

Install linter:
```
go install github.com/g4s8/envdoc/docenv@latest
```

Flags:
- `env-name` sets custom env tag name (default `env`)

```
go vet go vet -vettool=docenv -docenv.env-name=foo ./config.go
```
11 changes: 11 additions & 0 deletions docenv/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package main

import (
"github.com/g4s8/envdoc/linter"
"golang.org/x/tools/go/analysis/unitchecker"
)

func main() {
analyzer := linter.NewAnlyzer(true)
unitchecker.Main(analyzer)
}
34 changes: 34 additions & 0 deletions linter/analyzer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package linter

import (
"golang.org/x/tools/go/analysis"
)

// Option is a linter configuration option.
type Option func(*linter)

// WithEnvName sets custom env tag name for linter.
func WithEnvName(name string) Option {
return func(l *linter) {
l.envName = name
}
}

// NewAnlyzer creates a new linter analyzer.
func NewAnlyzer(parseFlags bool, opts ...Option) *analysis.Analyzer {
l := &linter{
envName: "env",
}
for _, opt := range opts {
opt(l)
}
a := &analysis.Analyzer{
Name: "docenv",
Doc: "check that all environment variables are documented",
Run: l.run,
}
if parseFlags {
a.Flags.StringVar(&l.envName, "env-name", l.envName, "environment variable tag name")
}
return a
}
63 changes: 63 additions & 0 deletions linter/linter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package linter

import (
"fmt"
"go/ast"
"strings"

"golang.org/x/tools/go/analysis"
)

type linter struct {
envName string
}

func (l *linter) run(pass *analysis.Pass) (interface{}, error) {
tagStr := fmt.Sprintf("%s:", l.envName)
for _, f := range pass.Files {
ast.Inspect(f, func(n ast.Node) bool {
field, ok := n.(*ast.Field)
if !ok {
return true
}

if field.Tag == nil {
return true
}

tag := field.Tag.Value
if !strings.Contains(tag, tagStr) {
return true
}

if !checkFieldDoc(field) {
names := fieldNames(field)
pass.Reportf(field.Pos(),
"field `%s` with `env` tag should have a documentation comment", names)
}

return true
})
}
return nil, nil
}

func fieldNames(f *ast.Field) string {
var names []string
for _, name := range f.Names {
names = append(names, name.Name)
}
return strings.Join(names, ", ")
}

func checkFieldDoc(f *ast.Field) bool {
if f.Doc != nil && strings.TrimSpace(f.Doc.Text()) != "" {
return true
}

if f.Comment != nil && strings.TrimSpace(f.Comment.Text()) != "" {
return true
}

return false
}

0 comments on commit 44b51d9

Please sign in to comment.