Skip to content

Commit

Permalink
MAJOR: add aspell as additional check for spelling errors
Browse files Browse the repository at this point in the history
  • Loading branch information
oktalz committed Aug 8, 2024
1 parent 6501643 commit 40c36c9
Show file tree
Hide file tree
Showing 17 changed files with 763 additions and 97 deletions.
34 changes: 34 additions & 0 deletions .aspell.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
mode: all
min_length: 3
ignore:
- '*_test.go'
allowed:
- aspell
- repo
- yaml
- config
- Github
- Gitlab
- env
- failsafe
- golang
- mkdir
- WORKDIR
- apk
- ENTRYPOINT
- ubuntu
- golangci
- splitted
- sudo
- mri
- IID
- repo
- OPTIM
- Submatch
- Lshortfile
- MAXSUBJECTLEN
- MAXSUBJECTPARTS
- MINSUBJECTLEN
- MINSUBJECTPARTS
- malformatted
- cmdline
13 changes: 11 additions & 2 deletions .github/workflows/actions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version-file: 'go.mod'
check-latest: true
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
uses: golangci/golangci-lint-action@v6
go_build:
name: Go build
runs-on: ubuntu-latest
Expand All @@ -32,6 +37,8 @@ jobs:
runs-on: ubuntu-latest
needs: ["go_build"]
steps:
- name: Install Aspell
run: sudo apt-get update && sudo apt-get install -y aspell aspell-en
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
Expand All @@ -47,10 +54,12 @@ jobs:
go test ./...
check_commit:
if: ${{ github.event_name == 'pull_request' }}
name: HAProxy check commit message
name: HAProxy check commit policy
runs-on: ubuntu-latest
needs: ["go_build"]
steps:
- name: Install Aspell
run: sudo apt-get update && sudo apt-get install -y aspell aspell-en
- uses: actions/checkout@v4
with:
fetch-depth: 0
Expand Down
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
FROM golang:alpine as builder
FROM golang:alpine AS builder
RUN mkdir /build
ADD . /build/
WORKDIR /build
RUN go build -o check

FROM alpine:latest
RUN apk --no-cache add aspell aspell-en
COPY --from=builder /build/check /check
WORKDIR /
ENTRYPOINT ["/check"]
34 changes: 33 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ bug: fix set-var parsing bug in config-parser
```
BUG/MEDIUM: fix set-var
```
- Unkown severity
- Unknown severity
```
BUG/MODERATE: fix set-var parsing bug in config-parser
```
Expand Down Expand Up @@ -95,3 +95,35 @@ TagOrder:
### Optional parameters

The program accepts an optional parameter to specify the location (path) of the base of the git repository. This can be useful in certain cases where the checked-out repo is in a non-standard location within the CI environment, compared to the running path from which the check-commit binary is being invoked.

### aspell

to check also spellcheck errors aspell was added. it can be configured with `.aspell.yml`

example
```yaml
mode: subject
min_length: 3
ignore:
- go.mod
- go.sum
- '*test.go'
- 'gen/*'
allowed:
- aspell
- config
```

`min_length` is minimal word size that is checked (default: 3)

`mode` can be set as

- `subject`
- `default` option
- only subject of commit message will be checked
- `commit`
- whole commit message will be checked
- `all`
- both commit message and all code committed
- `disabled`
- check is disabled
163 changes: 163 additions & 0 deletions aspell/aspell.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package aspell

import (
"bytes"
"fmt"
"log"
"os/exec"
"slices"
"sort"
"strings"

"check-commit/match"

"github.com/fatih/camelcase"
)

type Aspell struct {
Mode mode `yaml:"mode"`
MinLength int `yaml:"min_length"`
Ignore []string `yaml:"ignore"`
AllowedWords []string `yaml:"allowed"`
HelpText string `yaml:"-"`
}

var (
camelCaseOK = map[string]struct{}{
"HAProxy": {},
"golang": {},
"ascii": {},
"api": {},
}
camelCaseNotOK = map[string]struct{}{}
)

func (a Aspell) checkSingle(data string, allowedWords []string) error {
var words []string
var badWords []string

checkRes, err := checkWithAspellExec(data)
if checkRes != "" {
words = strings.Split(checkRes, "\n")
}
if err != nil {
return err
}

for _, word := range words {
wordLower := strings.ToLower(word)
if len(word) < a.MinLength {
continue
}
if _, ok := camelCaseNotOK[wordLower]; ok {
badWords = append(badWords, wordLower)
continue
}
if _, ok := camelCaseOK[wordLower]; ok {
continue
}
if slices.Contains(a.AllowedWords, wordLower) || slices.Contains(allowedWords, wordLower) {
continue
}
splitted := camelcase.Split(word)
if len(splitted) < 2 {
splitted = strings.FieldsFunc(word, func(r rune) bool {
return r == '_' || r == '-'
})
}
if len(splitted) > 1 {
for _, s := range splitted {
er := a.checkSingle(s, allowedWords)
if er != nil {
camelCaseNotOK[wordLower] = struct{}{}
badWords = append(badWords, word+":"+s)
break
}
}
} else {
camelCaseNotOK[wordLower] = struct{}{}
badWords = append(badWords, word)
}
}

if len(badWords) > 0 {
m := map[string]struct{}{}
for _, w := range badWords {
m[w] = struct{}{}
}
badWords = []string{}
for k := range m {
badWords = append(badWords, k)
}
sort.Strings(badWords)
return fmt.Errorf("aspell: %s", badWords)
}
return nil
}

func (a Aspell) Check(subjects []string, commitsFull []string, content []map[string]string) error {
var response string
var checks []string
switch a.Mode {
case modeDisabled:
return nil
case modeSubject:
checks = subjects
case modeCommit:
checks = commitsFull
case modeAll:
for _, file := range content {
for name, v := range file {
nextFile := false
for _, filter := range a.Ignore {
if match.MatchFilter(name, filter) {
// log.Println("File", name, "in ignore list")
nextFile = true
continue
}
}
if nextFile {
continue
}
var imports []string
if strings.HasSuffix(name, ".go") {
imports = match.GetImportWordsFromGoFile(name)
}
if err := a.checkSingle(v, imports); err != nil {
log.Println(name, err.Error())
response += fmt.Sprintf("%s\n", err)
}
}
}
checks = []string{}
default:
checks = subjects
}

for _, subject := range checks {
if err := a.checkSingle(subject, []string{}); err != nil {
response += fmt.Sprintf("%s\n", err)
}
}

if len(response) > 0 {
return fmt.Errorf("%s", response)
}
return nil
}

func checkWithAspellExec(subject string) (string, error) {
cmd := exec.Command("aspell", "--list")
cmd.Stdin = strings.NewReader(subject)

var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
log.Printf("aspell error: %s, stderr: %s", err, stderr.String())
return "", err
}

return stdout.String() + stderr.String(), nil
}
33 changes: 33 additions & 0 deletions aspell/aspell_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package aspell

import "testing"

func Test_checkWithAspell(t *testing.T) {
aspell := Aspell{
Mode: modeSubject,
MinLength: 3,
AllowedWords: []string{"config"},
}
tests := []struct {
name string
subject string
wantErr bool
}{
{"OK 1", "BUG/MEDIUM: config: add default location of path to the configuration file", false},
{"OK 2", "BUG/MEDIUM: config: add default location of path to the configuration file xtra", false},
{"error - flie", "BUG/MEDIUM: config: add default location of path to the configuration flie", true},
{"error - locatoin", "CLEANUP/MEDIUM: config: add default locatoin of path to the configuration file", true},
{"error - locatoin+flie", "CLEANUP/MEDIUM: config: add default locatoin of path to the configuration flie", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := aspell.checkSingle(tt.subject, []string{"xtra"})
if tt.wantErr && err == nil {
t.Errorf("checkWithAspell() error = %v, wantErr %v", err, tt.wantErr)
}
if !tt.wantErr && err != nil {
t.Errorf("checkWithAspell() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
10 changes: 10 additions & 0 deletions aspell/mode.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package aspell

type mode string

const (
modeDisabled mode = "disabled"
modeSubject mode = "subject"
modeCommit mode = "commit"
modeAll mode = "all"
)
Loading

0 comments on commit 40c36c9

Please sign in to comment.