|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +Local Beach is a Docker-based development environment for Neos CMS and Flow Framework. It's a CLI tool written in Go that manages local development instances using Docker containers (Nginx, PHP, Redis, and MySQL). |
| 8 | + |
| 9 | +The tool provides commands to initialize, start, stop, and manage Neos/Flow projects locally, with features like resource synchronization with Beach cloud storage, HTTPS setup, and Docker container orchestration. |
| 10 | + |
| 11 | +## Build and Development Commands |
| 12 | + |
| 13 | +### Building the Project |
| 14 | + |
| 15 | +**Full build (with asset generation):** |
| 16 | +```bash |
| 17 | +make |
| 18 | +``` |
| 19 | +This runs: |
| 20 | +- `rm -f assets/compiled.go` - removes compiled assets |
| 21 | +- `go generate -v` - generates embedded assets using vfsgen |
| 22 | +- `go install -v` - installs dependencies |
| 23 | +- `go build -v -ldflags "-X github.com/flownative/localbeach/pkg/version.Version=dev" -o beach` - builds binary |
| 24 | + |
| 25 | +**Quick compile (without regenerating assets):** |
| 26 | +```bash |
| 27 | +make compile |
| 28 | +``` |
| 29 | +Use this during development when assets haven't changed. |
| 30 | + |
| 31 | +### Testing |
| 32 | + |
| 33 | +Currently no test files exist in the project. When running `go test ./...`, note that there's a known build failure in `cmd/beach/cmd/resource-upload.go:84:3` due to a Printf formatting directive in a logrus.Fatal call. |
| 34 | + |
| 35 | +## Architecture |
| 36 | + |
| 37 | +### Project Structure |
| 38 | + |
| 39 | +- **`cmd/beach/cmd/`** - Cobra-based CLI commands (each command in its own file) |
| 40 | +- **`pkg/beachsandbox/`** - Core sandbox abstraction that represents a Local Beach project instance |
| 41 | +- **`pkg/exec/`** - Docker command execution wrapper |
| 42 | +- **`pkg/path/`** - Platform-specific path handling (Darwin/Linux) |
| 43 | +- **`pkg/version/`** - Version information |
| 44 | +- **`assets/`** - Embedded template files (Docker Compose configs, env templates, etc.) |
| 45 | + |
| 46 | +### Key Concepts |
| 47 | + |
| 48 | +**BeachSandbox**: The central abstraction representing a Local Beach project. Key responsibilities: |
| 49 | +- Project detection via `.localbeach.docker-compose.yaml` marker file |
| 50 | +- Environment variable loading from `.localbeach.dist.env`, `.localbeach.env`, `.env` (in that order) |
| 51 | +- Flow/Neos installation detection |
| 52 | +- Docker Compose file path management |
| 53 | + |
| 54 | +**Project Detection**: Commands traverse up from the current directory looking for `.localbeach.docker-compose.yaml` to find the project root (see `pkg/beachsandbox/helpers.go:detectProjectRootPath`). |
| 55 | + |
| 56 | +**Asset Embedding**: Template files in `assets/` are compiled into the binary using vfsgen. During build, `go generate` runs `assets_generate.go` which embeds all files from `assets/` into `assets/compiled.go`. |
| 57 | + |
| 58 | +### Docker Architecture |
| 59 | + |
| 60 | +Local Beach uses a two-tier Docker setup: |
| 61 | + |
| 62 | +1. **Global infrastructure** (started by `beach start`): |
| 63 | + - `local_beach_nginx` - Reverse proxy for all projects |
| 64 | + - `local_beach_database` - Shared MySQL database server |
| 65 | + - Managed by `assets/local-beach/docker-compose.yml` |
| 66 | + |
| 67 | +2. **Project-specific containers**: |
| 68 | + - Defined in `.localbeach.docker-compose.yaml` per project |
| 69 | + - Each project gets its own PHP-FPM container and services |
| 70 | + - Projects access the database at `http://{project-name}.localbeach.net` |
| 71 | + |
| 72 | +### Important File Locations |
| 73 | + |
| 74 | +**macOS:** |
| 75 | +- Base path: `~/Library/Application Support/Flownative/Local Beach/` |
| 76 | + |
| 77 | +**Linux/Other:** |
| 78 | +- Base path: `~/.Flownative/Local Beach/` |
| 79 | + |
| 80 | +These paths are defined in `pkg/path/path_darwin.go` and `pkg/path/path_linux.go`. |
| 81 | + |
| 82 | +## Common Development Patterns |
| 83 | + |
| 84 | +### Adding a New Command |
| 85 | + |
| 86 | +1. Create a new file in `cmd/beach/cmd/` (e.g., `my-command.go`) |
| 87 | +2. Define a `cobra.Command` struct with Use, Short, Long, Args, and Run fields |
| 88 | +3. Add `rootCmd.AddCommand(myCmd)` in the `init()` function |
| 89 | +4. Implement the `handleMyCommandRun` function |
| 90 | +5. Use `beachsandbox.GetActiveSandbox()` to get the current project context if needed |
| 91 | +6. Use `pkg/exec.RunCommand()` or `pkg/exec.RunInteractiveCommand()` for Docker operations |
| 92 | + |
| 93 | +### Environment Variable Handling |
| 94 | + |
| 95 | +Environment files are loaded in order (later files override earlier ones): |
| 96 | +1. `.localbeach.dist.env` - Committed defaults |
| 97 | +2. `.localbeach.env` - Local overrides |
| 98 | +3. `.env` - Additional local config |
| 99 | + |
| 100 | +Variables are parsed and set via `loadLocalBeachEnvironment()` in `pkg/beachsandbox/helpers.go`. |
| 101 | + |
| 102 | +### Resource Path Calculation |
| 103 | + |
| 104 | +Flow/Neos persistent resources use a hash-based directory structure. The `getRelativePersistentResourcePathByHash()` function in `cmd/beach/cmd/helpers.go` converts resource hashes to their filesystem path structure. |
| 105 | + |
| 106 | +### Docker Command Execution and TTY Detection |
| 107 | + |
| 108 | +The `pkg/exec` package provides two methods for executing Docker commands: |
| 109 | + |
| 110 | +- **`RunInteractiveCommand()`**: Connects stdin/stdout/stderr for interactive sessions |
| 111 | +- **`RunCommand()`**: Captures output without connecting stdin |
| 112 | + |
| 113 | +**TTY Detection Pattern** (see `cmd/beach/cmd/exec.go`): |
| 114 | + |
| 115 | +Commands that need to work both interactively (user's terminal) and programmatically (automation tools, Claude Code) should detect TTY availability: |
| 116 | + |
| 117 | +```go |
| 118 | +// Use syscall.TIOCGETA ioctl for reliable TTY detection |
| 119 | +var termios syscall.Termios |
| 120 | +_, _, errno := syscall.Syscall6(syscall.SYS_IOCTL, os.Stdin.Fd(), |
| 121 | + syscall.TIOCGETA, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) |
| 122 | +isTTY := errno == 0 |
| 123 | +``` |
| 124 | + |
| 125 | +This is more reliable than `os.Stat().Mode() & os.ModeCharDevice` which can incorrectly report TTY availability. |
| 126 | + |
| 127 | +**Docker Exec Flags**: |
| 128 | +- Interactive (TTY available): Use `-t -i` flags (allocate pseudo-TTY + keep stdin open) |
| 129 | +- Non-interactive: Omit all flags (Docker's `-t` flag requires a real TTY) |
| 130 | + |
| 131 | +**Error Handling**: When using `RunCommand()` in non-TTY mode, print output before checking errors so users see command output even on failure. |
| 132 | + |
| 133 | +## Version Management |
| 134 | + |
| 135 | +The version is injected at build time via ldflags: |
| 136 | +``` |
| 137 | +-ldflags "-X github.com/flownative/localbeach/pkg/version.Version=dev" |
| 138 | +``` |
| 139 | + |
| 140 | +For releases, replace "dev" with the actual version number. |
0 commit comments