Add devcontainer configuration for automatic bd setup
- Create .devcontainer/devcontainer.json with Go 1.23 environment - Add setup.sh to build bd from source and install git hooks - Add devcontainer README with documentation - Update main README to mention devcontainer support - Resolves bd-ry1u and GitHub issue #229
This commit is contained in:
File diff suppressed because one or more lines are too long
78
.devcontainer/README.md
Normal file
78
.devcontainer/README.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# beads Development Container
|
||||
|
||||
This devcontainer configuration provides a fully-configured development environment for beads with:
|
||||
|
||||
- Go 1.23 development environment
|
||||
- bd CLI built and installed from source
|
||||
- Git hooks automatically installed
|
||||
- All dependencies pre-installed
|
||||
|
||||
## Quick Start
|
||||
|
||||
### GitHub Codespaces
|
||||
|
||||
1. Click the "Code" button on GitHub
|
||||
2. Select "Create codespace on main"
|
||||
3. Wait for the container to build (~2-3 minutes)
|
||||
4. The environment will be ready with bd installed and configured
|
||||
|
||||
### VS Code Remote Containers
|
||||
|
||||
1. Install the [Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension
|
||||
2. Open the beads repository in VS Code
|
||||
3. Click "Reopen in Container" when prompted (or use Command Palette: "Remote-Containers: Reopen in Container")
|
||||
4. Wait for the container to build
|
||||
|
||||
## What Gets Installed
|
||||
|
||||
The `setup.sh` script automatically:
|
||||
|
||||
1. Builds bd from source (`go build ./cmd/bd`)
|
||||
2. Installs bd to `/usr/local/bin/bd`
|
||||
3. Runs `bd init --quiet` (non-interactive initialization)
|
||||
4. Installs git hooks from `examples/git-hooks/`
|
||||
5. Downloads Go module dependencies
|
||||
|
||||
## Verification
|
||||
|
||||
After the container starts, verify everything works:
|
||||
|
||||
```bash
|
||||
# Check bd is installed
|
||||
bd --version
|
||||
|
||||
# Check for ready tasks
|
||||
bd ready
|
||||
|
||||
# View project stats
|
||||
bd stats
|
||||
```
|
||||
|
||||
## Git Configuration
|
||||
|
||||
Your local `.gitconfig` is mounted into the container so your git identity is preserved. If you need to configure git:
|
||||
|
||||
```bash
|
||||
git config --global user.name "Your Name"
|
||||
git config --global user.email "your.email@example.com"
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**bd command not found:**
|
||||
- The setup script should install bd automatically
|
||||
- Manually run: `bash .devcontainer/setup.sh`
|
||||
|
||||
**Git hooks not working:**
|
||||
- Check if hooks are installed: `ls -la .git/hooks/`
|
||||
- Manually install: `bash examples/git-hooks/install.sh`
|
||||
|
||||
**Container fails to build:**
|
||||
- Check the container logs for specific errors
|
||||
- Ensure Docker/Podman is running and has sufficient resources
|
||||
- Try rebuilding: Command Palette → "Remote-Containers: Rebuild Container"
|
||||
|
||||
## Related Issues
|
||||
|
||||
- GitHub Issue [#229](https://github.com/steveyegge/beads/issues/229): Git hooks not available in devcontainers
|
||||
- bd-ry1u: Publish official devcontainer configuration
|
||||
31
.devcontainer/devcontainer.json
Normal file
31
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "beads Development Container",
|
||||
"image": "mcr.microsoft.com/devcontainers/go:1-1.23-bookworm",
|
||||
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/git:1": {
|
||||
"version": "latest"
|
||||
}
|
||||
},
|
||||
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"golang.go"
|
||||
],
|
||||
"settings": {
|
||||
"go.toolsManagement.checkForUpdates": "local",
|
||||
"go.useLanguageServer": true,
|
||||
"go.gopath": "/go"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"postCreateCommand": "bash .devcontainer/setup.sh",
|
||||
|
||||
"remoteUser": "vscode",
|
||||
|
||||
"mounts": [
|
||||
"source=${localEnv:HOME}${localEnv:USERPROFILE}/.gitconfig,target=/home/vscode/.gitconfig,type=bind,consistency=cached"
|
||||
]
|
||||
}
|
||||
33
.devcontainer/setup.sh
Normal file
33
.devcontainer/setup.sh
Normal file
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "🔧 Building bd from source..."
|
||||
go build -o bd ./cmd/bd
|
||||
|
||||
echo "📦 Installing bd globally..."
|
||||
sudo mv bd /usr/local/bin/bd
|
||||
sudo chmod +x /usr/local/bin/bd
|
||||
|
||||
echo "✅ Verifying bd installation..."
|
||||
bd --version
|
||||
|
||||
echo "🎯 Initializing bd (non-interactive)..."
|
||||
if [ ! -d .beads ]; then
|
||||
bd init --quiet
|
||||
else
|
||||
echo "bd already initialized"
|
||||
fi
|
||||
|
||||
echo "🪝 Installing git hooks..."
|
||||
if [ -f examples/git-hooks/install.sh ]; then
|
||||
bash examples/git-hooks/install.sh
|
||||
echo "Git hooks installed successfully"
|
||||
else
|
||||
echo "⚠️ Git hooks installer not found, skipping..."
|
||||
fi
|
||||
|
||||
echo "📚 Installing Go dependencies..."
|
||||
go mod download
|
||||
|
||||
echo "✨ Development environment ready!"
|
||||
echo "Run 'bd ready' to see available tasks"
|
||||
51
AGENTS.md
51
AGENTS.md
@@ -600,6 +600,57 @@ bd sync --merge
|
||||
|
||||
**See [docs/PROTECTED_BRANCHES.md](docs/PROTECTED_BRANCHES.md) for complete setup guide, troubleshooting, and examples.**
|
||||
|
||||
### Landing the Plane
|
||||
|
||||
**When the user says "let's land the plane"**, follow this clean session-ending protocol:
|
||||
|
||||
1. **File beads issues for any remaining work** that needs follow-up
|
||||
2. **Ensure all quality gates pass** (only if code changes were made) - run tests, linters, builds (file P0 issues if broken)
|
||||
3. **Update beads issues** - close finished work, update status
|
||||
4. **Git pull & rebase**
|
||||
5. **bd sync** - verify both import and export succeeded
|
||||
6. **Git push** - redo rebase if necessary until it succeeds
|
||||
7. **Double-check for untracked files** (beads files often get dirty again after git push)
|
||||
8. **Ensure no untracked files remain**
|
||||
9. **Choose a follow-up issue for next session**
|
||||
- Provide a prompt for the user to give to you in the next session
|
||||
- Format: "Continue work on bd-X: [issue title]. [Brief context about what's been done and what's next]"
|
||||
|
||||
**Example "land the plane" session:**
|
||||
```bash
|
||||
# 1. File remaining work
|
||||
bd create "Add integration tests for sync" -t task -p 2 --json
|
||||
|
||||
# 2. Run quality gates
|
||||
go test ./...
|
||||
golangci-lint run ./...
|
||||
|
||||
# 3. Close finished issues
|
||||
bd close bd-42 bd-43 --reason "Completed" --json
|
||||
|
||||
# 4. Pull & rebase
|
||||
git pull --rebase
|
||||
|
||||
# 5. Sync beads
|
||||
bd sync
|
||||
|
||||
# 6. Push
|
||||
git push
|
||||
|
||||
# 7-8. Verify clean state
|
||||
git status
|
||||
|
||||
# 9. Choose next work
|
||||
bd ready --json
|
||||
bd show bd-44 --json
|
||||
```
|
||||
|
||||
**Then provide the user with:**
|
||||
- Summary of what was completed this session
|
||||
- What issues were filed for follow-up
|
||||
- Status of quality gates (all passing / issues filed)
|
||||
- Recommended prompt for next session
|
||||
|
||||
### Agent Session Workflow
|
||||
|
||||
**IMPORTANT for AI agents:** When you finish making issue changes, always run:
|
||||
|
||||
@@ -127,6 +127,8 @@ echo "BEFORE ANYTHING ELSE: run 'bd onboard' and follow the instructions" >> AGE
|
||||
|
||||
**For new repo clones:** Run `bd init` (or `bd init --quiet` for agents) to import existing issues from `.beads/issues.jsonl` automatically.
|
||||
|
||||
**Using devcontainers?** Open the repository in a devcontainer (GitHub Codespaces or VS Code Remote Containers) and bd will be automatically installed with git hooks configured. See [.devcontainer/README.md](.devcontainer/README.md) for details.
|
||||
|
||||
Most tasks will be created and managed by agents during conversations. You can check on things with:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -306,11 +306,25 @@ Output to stdout by default, or use -o flag for file output.`,
|
||||
|
||||
// Set appropriate file permissions (0600: rw-------)
|
||||
if err := os.Chmod(finalPath, 0600); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Warning: failed to set file permissions: %v\n", err)
|
||||
fmt.Fprintf(os.Stderr, "Warning: failed to set file permissions: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Output statistics if JSON format requested
|
||||
// Verify JSONL file integrity after export
|
||||
actualCount, err := countIssuesInJSONL(finalPath)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: Export verification failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if actualCount != len(exportedIDs) {
|
||||
fmt.Fprintf(os.Stderr, "Error: Export verification failed\n")
|
||||
fmt.Fprintf(os.Stderr, " Expected: %d issues\n", len(exportedIDs))
|
||||
fmt.Fprintf(os.Stderr, " JSONL file: %d lines\n", actualCount)
|
||||
fmt.Fprintf(os.Stderr, " Mismatch indicates export failed to write all issues\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Output statistics if JSON format requested
|
||||
if jsonOutput {
|
||||
stats := map[string]interface{}{
|
||||
"success": true,
|
||||
|
||||
@@ -257,4 +257,67 @@ func TestExportCommand(t *testing.T) {
|
||||
t.Errorf("JSONL file was modified! Expected 2 issues, got %d", countAfter)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("verify JSONL line count matches exported count", func(t *testing.T) {
|
||||
exportPath := filepath.Join(tmpDir, "export_verify.jsonl")
|
||||
|
||||
// Clear export hashes to force re-export
|
||||
if err := s.ClearAllExportHashes(ctx); err != nil {
|
||||
t.Fatalf("Failed to clear export hashes: %v", err)
|
||||
}
|
||||
|
||||
store = s
|
||||
dbPath = testDB
|
||||
exportCmd.Flags().Set("output", exportPath)
|
||||
exportCmd.Run(exportCmd, []string{})
|
||||
|
||||
// Verify the exported file has exactly 2 lines
|
||||
actualCount, err := countIssuesInJSONL(exportPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to count issues in JSONL: %v", err)
|
||||
}
|
||||
if actualCount != 2 {
|
||||
t.Errorf("Expected 2 issues in JSONL, got %d", actualCount)
|
||||
}
|
||||
|
||||
// Simulate corrupted export by truncating file
|
||||
corruptedPath := filepath.Join(tmpDir, "export_corrupted.jsonl")
|
||||
|
||||
// First export normally
|
||||
if err := s.ClearAllExportHashes(ctx); err != nil {
|
||||
t.Fatalf("Failed to clear export hashes: %v", err)
|
||||
}
|
||||
store = s
|
||||
exportCmd.Flags().Set("output", corruptedPath)
|
||||
exportCmd.Run(exportCmd, []string{})
|
||||
|
||||
// Now manually corrupt it by removing one line
|
||||
file, err := os.Open(corruptedPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to open file for corruption: %v", err)
|
||||
}
|
||||
scanner := bufio.NewScanner(file)
|
||||
var lines []string
|
||||
for scanner.Scan() {
|
||||
lines = append(lines, scanner.Text())
|
||||
}
|
||||
file.Close()
|
||||
|
||||
// Write back only first line (simulating partial write)
|
||||
corruptedFile, err := os.Create(corruptedPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create corrupted file: %v", err)
|
||||
}
|
||||
corruptedFile.WriteString(lines[0] + "\n")
|
||||
corruptedFile.Close()
|
||||
|
||||
// Verify countIssuesInJSONL detects the corruption
|
||||
count, err := countIssuesInJSONL(corruptedPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to count corrupted file: %v", err)
|
||||
}
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 line in corrupted file, got %d", count)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -141,6 +141,12 @@ var rootCmd = &cobra.Command{
|
||||
noAutoImport = true
|
||||
}
|
||||
|
||||
// Force direct mode for human-only interactive commands
|
||||
// edit: can take minutes in $EDITOR, daemon connection times out (GH #227)
|
||||
if cmd.Name() == "edit" {
|
||||
noDaemon = true
|
||||
}
|
||||
|
||||
// Set auto-flush based on flag (invert no-auto-flush)
|
||||
autoFlushEnabled = !noAutoFlush
|
||||
|
||||
|
||||
Reference in New Issue
Block a user