chore: bump version to 4.5.0

Bug fixes:
- Fix inverted pager detection logic (returned error instead of path)
- Fix repo.Clone ignoring destination directory parameter
- Fix sheet loading using append on pre-sized slices
- Clean up partial files on copy failure
- Trim whitespace from editor config

Security:
- Add path traversal protection for cheatsheet names

Performance:
- Move regex compilation outside search loop
- Replace string concatenation with strings.Join in search

Build:
- Remove go:generate; embed config and usage as string literals
- Parallelize release builds
- Add fuzz testing infrastructure

Testing:
- Improve test coverage from 38.9% to 50.2%
- Add fuzz tests for search, filter, tags, and validation

Documentation:
- Fix inaccurate code examples in HACKING.md
- Add missing --conf and --all options to man page
- Add ADRs for path traversal, env parsing, and search parallelization
- Update CONTRIBUTING.md to reflect project policy

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Christopher Allen Lane
2026-02-14 19:56:19 -05:00
parent 7908a678df
commit cc85a4bdb1
69 changed files with 4802 additions and 577 deletions

View File

@@ -8,11 +8,11 @@ import (
"github.com/go-git/go-git/v5"
)
// Clone clones the repo available at `url`
func Clone(url string) error {
// Clone clones the community cheatsheets repository to the specified directory
func Clone(dir string) error {
// clone the community cheatsheets
_, err := git.PlainClone(url, false, &git.CloneOptions{
_, err := git.PlainClone(dir, false, &git.CloneOptions{
URL: "https://github.com/cheat/cheatsheets.git",
Depth: 1,
Progress: os.Stdout,

View File

@@ -0,0 +1,80 @@
//go:build integration
// +build integration
package repo
import (
"os"
"path/filepath"
"testing"
)
// TestCloneIntegration performs a real clone operation to verify functionality
// Run with: go test -tags=integration ./internal/repo -v -run TestCloneIntegration
func TestCloneIntegration(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
// Create a temporary directory
tmpDir, err := os.MkdirTemp("", "cheat-clone-integration-*")
if err != nil {
t.Fatalf("failed to create temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
destDir := filepath.Join(tmpDir, "cheatsheets")
t.Logf("Cloning to: %s", destDir)
// Perform the actual clone
err = Clone(destDir)
if err != nil {
t.Fatalf("Clone() failed: %v", err)
}
// Verify the clone succeeded
info, err := os.Stat(destDir)
if err != nil {
t.Fatalf("destination directory not created: %v", err)
}
if !info.IsDir() {
t.Fatal("destination is not a directory")
}
// Check for .git directory
gitDir := filepath.Join(destDir, ".git")
if _, err := os.Stat(gitDir); err != nil {
t.Error(".git directory not found")
}
// Check for some expected cheatsheets
expectedFiles := []string{
"bash", // bash cheatsheet should exist
"git", // git cheatsheet should exist
"ls", // ls cheatsheet should exist
}
foundCount := 0
for _, file := range expectedFiles {
path := filepath.Join(destDir, file)
if _, err := os.Stat(path); err == nil {
foundCount++
}
}
if foundCount < 2 {
t.Errorf("expected at least 2 common cheatsheets, found %d", foundCount)
}
t.Log("Clone integration test passed!")
// Test cloning to existing directory (should fail)
err = Clone(destDir)
if err == nil {
t.Error("expected error when cloning to existing repository, got nil")
} else {
t.Logf("Expected error when cloning to existing dir: %v", err)
}
}

View File

@@ -0,0 +1,49 @@
package repo
import (
"os"
"path/filepath"
"testing"
)
// TestClone tests the Clone function
func TestClone(t *testing.T) {
// This test requires network access, so we'll only test error cases
// that don't require actual cloning
t.Run("clone to read-only directory", func(t *testing.T) {
if os.Getuid() == 0 {
t.Skip("Cannot test read-only directory as root")
}
// Create a temporary directory
tempDir, err := os.MkdirTemp("", "cheat-clone-test-*")
if err != nil {
t.Fatalf("failed to create temp dir: %v", err)
}
defer os.RemoveAll(tempDir)
// Create a read-only subdirectory
readOnlyDir := filepath.Join(tempDir, "readonly")
if err := os.Mkdir(readOnlyDir, 0555); err != nil {
t.Fatalf("failed to create read-only dir: %v", err)
}
// Attempt to clone to read-only directory
targetDir := filepath.Join(readOnlyDir, "cheatsheets")
err = Clone(targetDir)
// Should fail because we can't write to read-only directory
if err == nil {
t.Error("expected error when cloning to read-only directory, got nil")
}
})
t.Run("clone to invalid path", func(t *testing.T) {
// Try to clone to a path with null bytes (invalid on most filesystems)
err := Clone("/tmp/invalid\x00path")
if err == nil {
t.Error("expected error with invalid path, got nil")
}
})
}

View File

@@ -0,0 +1,177 @@
package repo
import (
"fmt"
"os"
"path/filepath"
"testing"
)
func TestGitDir(t *testing.T) {
// Create a temporary directory for testing
tempDir, err := os.MkdirTemp("", "cheat-test-*")
if err != nil {
t.Fatalf("failed to create temp dir: %v", err)
}
defer os.RemoveAll(tempDir)
// Create test directory structure
testDirs := []string{
filepath.Join(tempDir, ".git"),
filepath.Join(tempDir, ".git", "objects"),
filepath.Join(tempDir, ".git", "refs"),
filepath.Join(tempDir, "regular"),
filepath.Join(tempDir, "regular", ".git"),
filepath.Join(tempDir, "submodule"),
}
for _, dir := range testDirs {
if err := os.MkdirAll(dir, 0755); err != nil {
t.Fatalf("failed to create dir %s: %v", dir, err)
}
}
// Create test files
testFiles := map[string]string{
filepath.Join(tempDir, ".gitignore"): "*.tmp\n",
filepath.Join(tempDir, ".gitattributes"): "* text=auto\n",
filepath.Join(tempDir, "submodule", ".git"): "gitdir: ../.git/modules/submodule\n",
filepath.Join(tempDir, "regular", "sheet.txt"): "content\n",
}
for file, content := range testFiles {
if err := os.WriteFile(file, []byte(content), 0644); err != nil {
t.Fatalf("failed to create file %s: %v", file, err)
}
}
tests := []struct {
name string
path string
want bool
wantErr bool
}{
{
name: "not in git directory",
path: filepath.Join(tempDir, "regular", "sheet.txt"),
want: false,
},
{
name: "in .git directory",
path: filepath.Join(tempDir, ".git", "objects", "file"),
want: true,
},
{
name: "in .git/refs directory",
path: filepath.Join(tempDir, ".git", "refs", "heads", "main"),
want: true,
},
{
name: ".gitignore file",
path: filepath.Join(tempDir, ".gitignore"),
want: false,
},
{
name: ".gitattributes file",
path: filepath.Join(tempDir, ".gitattributes"),
want: false,
},
{
name: "submodule with .git file",
path: filepath.Join(tempDir, "submodule", "sheet.txt"),
want: false,
},
{
name: "path with .git in middle",
path: filepath.Join(tempDir, "regular", ".git", "sheet.txt"),
want: true,
},
{
name: "nonexistent path without .git",
path: filepath.Join(tempDir, "nonexistent", "file"),
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := GitDir(tt.path)
if (err != nil) != tt.wantErr {
t.Errorf("GitDir() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("GitDir() = %v, want %v", got, tt.want)
}
})
}
}
func TestGitDirEdgeCases(t *testing.T) {
// Test with paths that have .git but not as a directory separator
tests := []struct {
name string
path string
want bool
}{
{
name: "file ending with .git",
path: "/tmp/myfile.git",
want: false,
},
{
name: "directory ending with .git",
path: "/tmp/myrepo.git",
want: false,
},
{
name: ".github directory",
path: "/tmp/.github/workflows",
want: false,
},
{
name: "legitimate.git-repo name",
path: "/tmp/legitimate.git-repo/file",
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := GitDir(tt.path)
if err != nil {
// It's ok if the path doesn't exist for these edge case tests
return
}
if got != tt.want {
t.Errorf("GitDir(%q) = %v, want %v", tt.path, got, tt.want)
}
})
}
}
func TestGitDirPathSeparator(t *testing.T) {
// Test that the function correctly uses os.PathSeparator
// This is important for cross-platform compatibility
// Create a path with the wrong separator for the current OS
var wrongSep string
if os.PathSeparator == '/' {
wrongSep = `\`
} else {
wrongSep = `/`
}
// Path with wrong separator should not be detected as git dir
path := fmt.Sprintf("some%spath%s.git%sfile", wrongSep, wrongSep, wrongSep)
isGit, err := GitDir(path)
if err != nil {
// Path doesn't exist, which is fine
return
}
if isGit {
t.Errorf("GitDir() incorrectly detected git dir with wrong path separator")
}
}