Files
beads/cmd/bd/doctor/fix/permissions.go
Ryan 3c08e5eb9d DOCTOR IMPROVEMENTS: visual improvements/grouping + add comprehensive tests + fix gosec warnings (#656)
* test(doctor): add comprehensive tests for fix and check functions

Add edge case tests, e2e tests, and improve test coverage for:
- database_test.go: database integrity and sync checks
- git_test.go: git hooks, merge driver, sync branch tests
- gitignore_test.go: gitignore validation
- prefix_test.go: ID prefix handling
- fix/fix_test.go: fix operations
- fix/e2e_test.go: end-to-end fix scenarios
- fix/fix_edge_cases_test.go: edge case handling

* docs: add testing philosophy and anti-patterns guide

- Create TESTING_PHILOSOPHY.md covering test pyramid, priority matrix,
  what NOT to test, and 5 anti-patterns with code examples
- Add cross-reference from README_TESTING.md
- Document beads-specific guidance (well-covered areas vs gaps)
- Include target metrics (test-to-code ratio, execution time targets)

* chore: revert .beads/ to upstream/main state

* refactor(doctor): add category grouping and Ayu theme colors

- Add Category field to DoctorCheck for organizing checks by type
- Define category constants: Core, Git, Runtime, Data, Integration, Metadata
- Update thanks command to use shared Ayu color palette from internal/ui
- Simplify test fixtures by removing redundant test cases

* fix(doctor): prevent test fork bomb and fix test failures

- Add ErrTestBinary guard in getBdBinary() to prevent tests from
  recursively executing the test binary when calling bd subcommands
- Update claude_test.go to use new check names (CLI Availability,
  Prime Documentation)
- Fix syncbranch test path comparison by resolving symlinks
  (/var vs /private/var on macOS)
- Fix permissions check to use exact comparison instead of bitmask
- Fix UntrackedJSONL to use git commit --only to preserve staged changes
- Fix MergeDriver edge case test by making both .git dir and config
  read-only
- Add skipIfTestBinary helper for E2E tests that need real bd binary

* test(doctor): skip read-only config test in CI environments

GitHub Actions containers may have CAP_DAC_OVERRIDE or similar
capabilities that allow writing to read-only files, causing
the test to fail. Skip the test when CI=true or GITHUB_ACTIONS=true.
2025-12-20 03:10:06 -08:00

62 lines
1.8 KiB
Go

package fix
import (
"fmt"
"os"
"path/filepath"
)
// Permissions fixes file permission issues in the .beads directory
func Permissions(path string) error {
// Validate workspace
if err := validateBeadsWorkspace(path); err != nil {
return err
}
beadsDir := filepath.Join(path, ".beads")
// Check if .beads/ directory exists
// Use Lstat to detect symlinks - we shouldn't chmod symlinked directories
// as this would change the target's permissions (problematic on NixOS).
info, err := os.Lstat(beadsDir)
if err != nil {
return fmt.Errorf("failed to stat .beads directory: %w", err)
}
// Skip permission fixes for symlinked .beads directories (common on NixOS with home-manager)
if info.Mode()&os.ModeSymlink != 0 {
return nil // Symlink permissions are not meaningful on Unix
}
// Ensure .beads directory has exactly 0700 permissions (owner rwx only)
expectedDirMode := os.FileMode(0700)
if info.Mode().Perm() != expectedDirMode {
if err := os.Chmod(beadsDir, expectedDirMode); err != nil {
return fmt.Errorf("failed to fix .beads directory permissions: %w", err)
}
}
// Fix permissions on database file if it exists
// Use Lstat to detect symlinks - skip chmod for symlinked database files
dbPath := filepath.Join(beadsDir, "beads.db")
if dbInfo, err := os.Lstat(dbPath); err == nil {
// Skip permission fixes for symlinked database files (NixOS)
if dbInfo.Mode()&os.ModeSymlink != 0 {
return nil
}
// Ensure database has exactly 0600 permissions (owner rw only)
expectedFileMode := os.FileMode(0600)
currentPerms := dbInfo.Mode().Perm()
// Check if permissions are not exactly 0600
if currentPerms != expectedFileMode {
if err := os.Chmod(dbPath, expectedFileMode); err != nil {
return fmt.Errorf("failed to fix database permissions: %w", err)
}
}
}
return nil
}