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
7 changes: 7 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Enforce LF line endings for Go files and repository sources
* text=auto eol=lf
*.go text eol=lf
*.md text eol=lf
*.json text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
57 changes: 57 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: CI

on:
push:
branches:
- main
pull_request:
branches:
- main

permissions:
contents: read

jobs:
build-and-test:
name: Test & Build (${{ matrix.os }}, Go ${{ matrix.go-version }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
go-version: ['1.22.x', '1.23.x']

defaults:
run:
shell: bash

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
cache: true

- name: Check code formatting (gofmt)
run: |
UNFORMATTED=$(gofmt -l .)
if [ -n "$UNFORMATTED" ]; then
echo "Error: The following files are not formatted according to gofmt:"
echo "$UNFORMATTED"
exit 1
fi

- name: Verify module dependencies
run: go mod verify

- name: Run tests with race detector
run: go test -v -race ./...

- name: Build all packages
run: go build -v ./...

- name: Build CLI binary
run: go build -v -o ./bin/envguard ./cmd/envguard
1 change: 0 additions & 1 deletion cmd/envguard/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,3 @@ func main() {
code := cli.Run(os.Args[1:], os.Stdout, os.Stderr)
os.Exit(code)
}

2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module github.com/joaooncode/envguard

go 1.27.0
go 1.22.0
8 changes: 4 additions & 4 deletions internal/cli/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import (

// scanConfig holds the parsed options for scan/check commands.
type scanConfig struct {
path string
format string
severity string
noColor bool
path string
format string
severity string
noColor bool
}

func parseSeverity(s string) (scanner.Severity, bool) {
Expand Down
30 changes: 29 additions & 1 deletion internal/git/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ func (r *OSCommandRunner) Run(dir string, args ...string) ([]byte, []byte, int,

// normalizePath converts a path (relative to dir or absolute) to a slash-delimited relative path to repoRoot.
func normalizePath(repoRoot, dir, targetPath string) string {
if evalRoot, err := filepath.EvalSymlinks(repoRoot); err == nil {
repoRoot = evalRoot
}

var fullPath string
if filepath.IsAbs(targetPath) {
fullPath = targetPath
Expand All @@ -74,14 +78,38 @@ func normalizePath(repoRoot, dir, targetPath string) string {
fullPath = targetPath
}

if evalFull, err := filepath.EvalSymlinks(fullPath); err == nil {
fullPath = evalFull
} else {
parent := filepath.Dir(fullPath)
if evalParent, err := filepath.EvalSymlinks(parent); err == nil {
fullPath = filepath.Join(evalParent, filepath.Base(fullPath))
}
}

if repoRoot != "" {
if rel, err := filepath.Rel(repoRoot, fullPath); err == nil {
repoRootClean := filepath.Clean(repoRoot)
fullPathClean := filepath.Clean(fullPath)

volRepo := filepath.VolumeName(repoRootClean)
volFull := filepath.VolumeName(fullPathClean)
if volRepo != "" && strings.EqualFold(volRepo, volFull) && volRepo != volFull {
fullPathClean = volRepo + fullPathClean[len(volFull):]
}

if rel, err := filepath.Rel(repoRootClean, fullPathClean); err == nil && !strings.HasPrefix(rel, "..") {
cleanPath := filepath.ToSlash(rel)
cleanPath = strings.TrimPrefix(cleanPath, "./")
return cleanPath
}
}

if !filepath.IsAbs(targetPath) {
clean := filepath.ToSlash(filepath.Clean(targetPath))
clean = strings.TrimPrefix(clean, "./")
return clean
}

cleanPath := filepath.ToSlash(fullPath)
cleanPath = strings.TrimPrefix(cleanPath, "./")
return cleanPath
Expand Down
6 changes: 5 additions & 1 deletion internal/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,11 @@ func (c *GitClient) GetRepoRoot(dir string) (string, error) {
if err != nil || exitCode != 0 {
return "", fmt.Errorf("failed to get git repository root: %s (exit code %d)", strings.TrimSpace(string(stderr)), exitCode)
}
return filepath.Clean(strings.TrimSpace(string(stdout))), nil
root := filepath.Clean(strings.TrimSpace(string(stdout)))
if evalRoot, err := filepath.EvalSymlinks(root); err == nil {
return evalRoot, nil
}
return root, nil
}

// IsTracked reports whether the file at filePath is tracked in Git.
Expand Down
1 change: 0 additions & 1 deletion internal/reporter/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,3 @@ func (j *JSONReporter) Render(result *scanner.Result, w io.Writer) error {

return nil
}

1 change: 0 additions & 1 deletion internal/reporter/reporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,4 +293,3 @@ func TestJSONReporter_Render(t *testing.T) {
}
})
}

1 change: 0 additions & 1 deletion internal/reporter/terminal.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,3 @@ func (t *TerminalReporter) severityBadge(sev scanner.Severity) (icon string, bad
return t.c.dim("•"), t.c.dim(fmt.Sprintf("[%s]", sevUpper))
}
}

1 change: 0 additions & 1 deletion internal/scanner/finding.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,3 @@ func (r *Result) CalculateSummary() Summary {
r.Summary = CalculateSummary(r.Findings)
return r.Summary
}

12 changes: 6 additions & 6 deletions internal/scanner/scanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -368,12 +368,12 @@ type mockGitClient struct {
statusFn func(dir, filePath string) (git.FileStatus, error)
}

func (m *mockGitClient) IsAvailable() bool { return true }
func (m *mockGitClient) IsGitRepo(dir string) bool { return true }
func (m *mockGitClient) GetRepoRoot(dir string) (string, error) { return dir, nil }
func (m *mockGitClient) IsTracked(dir, filePath string) (bool, error) { return false, nil }
func (m *mockGitClient) IsStaged(dir, filePath string) (bool, error) { return false, nil }
func (m *mockGitClient) IsIgnored(dir, filePath string) (bool, error) { return false, nil }
func (m *mockGitClient) IsAvailable() bool { return true }
func (m *mockGitClient) IsGitRepo(dir string) bool { return true }
func (m *mockGitClient) GetRepoRoot(dir string) (string, error) { return dir, nil }
func (m *mockGitClient) IsTracked(dir, filePath string) (bool, error) { return false, nil }
func (m *mockGitClient) IsStaged(dir, filePath string) (bool, error) { return false, nil }
func (m *mockGitClient) IsIgnored(dir, filePath string) (bool, error) { return false, nil }
func (m *mockGitClient) GetFileStatus(dir, filePath string) (git.FileStatus, error) {
if m.statusFn != nil {
return m.statusFn(dir, filePath)
Expand Down
Loading