Skip to content

Commit f94557c

Browse files
authored
Merge pull request #180 from frankbria/feature/issue-174-setup-gitignore
fix(setup): generate .gitignore for new projects
2 parents 9408fdf + 28c955d commit f94557c

8 files changed

Lines changed: 244 additions & 6 deletions

File tree

CLAUDE.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ tmux attach -t <session-name>
169169

170170
### Running Tests
171171
```bash
172-
# Run all tests (499 tests)
172+
# Run all tests (510 tests)
173173
npm test
174174

175175
# Run specific test suites
@@ -459,7 +459,7 @@ Ralph uses advanced error detection with two-stage filtering to eliminate false
459459

460460
## Test Suite
461461

462-
### Test Files (499 tests total)
462+
### Test Files (510 tests total)
463463

464464
| File | Tests | Description |
465465
|------|-------|-------------|
@@ -472,10 +472,10 @@ Ralph uses advanced error detection with two-stage filtering to eliminate false
472472
| `test_rate_limiting.bats` | 15 | Rate limiting behavior |
473473
| `test_loop_execution.bats` | 20 | Integration tests |
474474
| `test_edge_cases.bats` | 25 | Edge case handling |
475-
| `test_installation.bats` | 14 | Global installation/uninstall workflows |
476-
| `test_project_setup.bats` | 44 | Project setup (setup.sh) validation + .ralphrc permissions |
475+
| `test_installation.bats` | 15 | Global installation/uninstall workflows + dotfile template copying (#174) |
476+
| `test_project_setup.bats` | 50 | Project setup (setup.sh) validation + .ralphrc permissions + .gitignore (#174) |
477477
| `test_prd_import.bats` | 33 | PRD import (ralph_import.sh) workflows + modern CLI tests |
478-
| `test_enable_core.bats` | 32 | Enable core library (idempotency, project detection, template generation) |
478+
| `test_enable_core.bats` | 36 | Enable core library (idempotency, project detection, template generation, .gitignore #174) |
479479
| `test_task_sources.bats` | 23 | Task sources (beads, GitHub, PRD extraction, normalization) |
480480
| `test_ralph_enable.bats` | 22 | Ralph enable integration tests (wizard, CI version, JSON output) |
481481
| `test_wizard_utils.bats` | 20 | Wizard utility functions (stdout/stderr separation, prompt functions) |

install.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,10 @@ create_install_dirs() {
108108
install_scripts() {
109109
log "INFO" "Installing Ralph scripts..."
110110

111-
# Copy templates to Ralph home
111+
# Copy templates to Ralph home (dotglob needed for dotfiles like .gitignore)
112+
shopt -s dotglob
112113
cp -r "$SCRIPT_DIR/templates/"* "$RALPH_HOME/templates/"
114+
shopt -u dotglob
113115

114116
# Copy lib scripts (response_analyzer.sh, circuit_breaker.sh)
115117
cp -r "$SCRIPT_DIR/lib/"* "$RALPH_HOME/lib/"

lib/enable_core.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,17 @@ enable_ralph_in_directory() {
777777
fix_plan_content=$(generate_fix_plan_md "$task_content")
778778
safe_create_file ".ralph/fix_plan.md" "$fix_plan_content"
779779

780+
# Copy .gitignore template to project root (if available)
781+
local templates_dir
782+
templates_dir=$(get_templates_dir 2>/dev/null) || true
783+
if [[ -n "$templates_dir" ]] && [[ -f "$templates_dir/.gitignore" ]]; then
784+
local gitignore_content
785+
gitignore_content=$(<"$templates_dir/.gitignore")
786+
safe_create_file ".gitignore" "$gitignore_content"
787+
else
788+
enable_log "WARN" ".gitignore template not found, skipping"
789+
fi
790+
780791
# Detect task sources for .ralphrc
781792
detect_task_sources
782793
local task_sources="local"

setup.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ cp "$TEMPLATES_DIR/fix_plan.md" .ralph/fix_plan.md
4747
cp "$TEMPLATES_DIR/AGENT.md" .ralph/AGENT.md
4848
cp -r "$TEMPLATES_DIR/specs"/* .ralph/specs/ 2>/dev/null || true
4949

50+
# Copy .gitignore template to project root (skip if one already exists)
51+
if [[ -f "$TEMPLATES_DIR/.gitignore" ]] && [[ ! -f ".gitignore" ]]; then
52+
cp "$TEMPLATES_DIR/.gitignore" .gitignore
53+
fi
54+
5055
# Generate .ralphrc configuration file
5156
# Source enable_core.sh if available for generate_ralphrc(), otherwise create inline
5257
if [[ -f "$LIB_DIR/enable_core.sh" ]]; then

templates/.gitignore

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Ralph generated files (inside .ralph/ subfolder)
2+
.ralph/.call_count
3+
.ralph/.last_reset
4+
.ralph/.exit_signals
5+
.ralph/status.json
6+
.ralph/.ralph_session
7+
.ralph/.ralph_session_history
8+
.ralph/.claude_session_id
9+
.ralph/.response_analysis
10+
.ralph/.circuit_breaker_state
11+
.ralph/.circuit_breaker_history
12+
13+
# Ralph logs and generated docs
14+
.ralph/logs/*
15+
!.ralph/logs/.gitkeep
16+
.ralph/docs/generated/*
17+
!.ralph/docs/generated/.gitkeep
18+
19+
# General logs
20+
*.log
21+
22+
# OS files
23+
.DS_Store
24+
Thumbs.db
25+
26+
# Temporary files
27+
*.tmp
28+
.temp/
29+
30+
# Node modules (if using Node.js projects)
31+
node_modules/
32+
33+
# Python cache (if using Python projects)
34+
__pycache__/
35+
*.pyc
36+
37+
# Rust build (if using Rust projects)
38+
target/
39+
40+
# IDE files
41+
.vscode/
42+
.idea/
43+
*.swp
44+
*.swo
45+
46+
# Ralph backup directories (created by migration)
47+
.ralph_backup_*

tests/integration/test_installation.bats

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ setup() {
3131
echo "# Mock PROMPT.md" > "$MOCK_SOURCE_DIR/templates/PROMPT.md"
3232
echo "# Mock fix_plan.md" > "$MOCK_SOURCE_DIR/templates/fix_plan.md"
3333
echo "# Mock AGENT.md" > "$MOCK_SOURCE_DIR/templates/AGENT.md"
34+
echo ".ralph/.call_count" > "$MOCK_SOURCE_DIR/templates/.gitignore"
3435

3536
# Create mock lib files
3637
cat > "$MOCK_SOURCE_DIR/lib/circuit_breaker.sh" << 'EOF'
@@ -254,6 +255,13 @@ run_install() {
254255
diff -q "$MOCK_SOURCE_DIR/templates/AGENT.md" "$TEST_RALPH_HOME/templates/AGENT.md"
255256
}
256257

258+
@test "install.sh copies dotfile templates like .gitignore" {
259+
run run_install
260+
261+
assert_file_exists "$TEST_RALPH_HOME/templates/.gitignore"
262+
diff -q "$MOCK_SOURCE_DIR/templates/.gitignore" "$TEST_RALPH_HOME/templates/.gitignore"
263+
}
264+
257265
@test "install.sh copies lib/ directory" {
258266
run run_install
259267

tests/integration/test_project_setup.bats

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,3 +543,86 @@ teardown() {
543543
# .ralphrc should reference the project name
544544
grep -q "my-custom-project" my-custom-project/.ralphrc
545545
}
546+
547+
# =============================================================================
548+
# Test: .gitignore Generation (Issue #174)
549+
# =============================================================================
550+
551+
@test "setup.sh creates .gitignore file" {
552+
# Create .gitignore template
553+
cat > templates/.gitignore << 'EOF'
554+
# Ralph generated files
555+
.ralph/.call_count
556+
.ralph/.last_reset
557+
.ralph/status.json
558+
EOF
559+
560+
run bash "$SETUP_SCRIPT" test-project
561+
562+
assert_success
563+
assert_file_exists "test-project/.gitignore"
564+
}
565+
566+
@test "setup.sh .gitignore contains Ralph runtime patterns" {
567+
cat > templates/.gitignore << 'EOF'
568+
.ralph/.call_count
569+
.ralph/.last_reset
570+
.ralph/status.json
571+
.ralph/.circuit_breaker_state
572+
EOF
573+
574+
bash "$SETUP_SCRIPT" test-project
575+
576+
grep -q ".ralph/.call_count" test-project/.gitignore
577+
grep -q ".ralph/.circuit_breaker_state" test-project/.gitignore
578+
}
579+
580+
@test "setup.sh .gitignore is committed in initial git commit" {
581+
cat > templates/.gitignore << 'EOF'
582+
.ralph/.call_count
583+
EOF
584+
585+
bash "$SETUP_SCRIPT" test-project
586+
587+
cd test-project
588+
run command git ls-files .gitignore
589+
590+
assert_success
591+
assert_equal "$output" ".gitignore"
592+
}
593+
594+
@test "setup.sh .gitignore content matches template" {
595+
cat > templates/.gitignore << 'EOF'
596+
# Ralph generated files
597+
.ralph/.call_count
598+
.ralph/.last_reset
599+
EOF
600+
601+
bash "$SETUP_SCRIPT" test-project
602+
603+
diff templates/.gitignore test-project/.gitignore
604+
}
605+
606+
@test "setup.sh succeeds when .gitignore template is missing" {
607+
# Do NOT create templates/.gitignore — should still succeed
608+
run bash "$SETUP_SCRIPT" test-project
609+
610+
assert_success
611+
# .gitignore should not exist since template was missing
612+
[[ ! -f "test-project/.gitignore" ]]
613+
}
614+
615+
@test "setup.sh preserves existing .gitignore on rerun" {
616+
echo ".ralph/.call_count" > templates/.gitignore
617+
618+
# First run creates the project with .gitignore
619+
bash "$SETUP_SCRIPT" test-project
620+
621+
# User customizes the .gitignore
622+
echo "my-custom-pattern" >> test-project/.gitignore
623+
624+
# Second run (rerun in existing directory) should not overwrite
625+
bash "$SETUP_SCRIPT" test-project
626+
627+
grep -q "my-custom-pattern" test-project/.gitignore
628+
}

tests/unit/test_enable_core.bats

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,25 @@ load '../helpers/fixtures'
77

88
# Path to enable_core.sh
99
ENABLE_CORE="${BATS_TEST_DIRNAME}/../../lib/enable_core.sh"
10+
ORIGINAL_HOME="$HOME"
1011

1112
setup() {
1213
# Create temporary test directory
1314
TEST_DIR="$(mktemp -d)"
1415
cd "$TEST_DIR"
1516

17+
# Isolate HOME so tests that write to ~/.ralph don't leak to real home dir
18+
export HOME="$TEST_DIR/home"
19+
mkdir -p "$HOME"
20+
1621
# Source the library (disable set -e for testing)
1722
set +e
1823
source "$ENABLE_CORE"
1924
set -e
2025
}
2126

2227
teardown() {
28+
export HOME="$ORIGINAL_HOME"
2329
if [[ -n "$TEST_DIR" ]] && [[ -d "$TEST_DIR" ]]; then
2430
cd /
2531
rm -rf "$TEST_DIR"
@@ -386,3 +392,79 @@ EOF
386392
content=$(cat test_file.txt)
387393
[[ "$content" == "original content" ]]
388394
}
395+
396+
# =============================================================================
397+
# .GITIGNORE CREATION (Issue #174) (4 tests)
398+
# =============================================================================
399+
400+
@test "enable_ralph_in_directory creates .gitignore when template exists" {
401+
# HOME is already isolated to TEST_DIR/home by setup()
402+
mkdir -p "$HOME/.ralph/templates"
403+
cat > "$HOME/.ralph/templates/.gitignore" << 'EOF'
404+
.ralph/.call_count
405+
.ralph/.last_reset
406+
.ralph/status.json
407+
EOF
408+
409+
export ENABLE_FORCE="false"
410+
export ENABLE_SKIP_TASKS="true"
411+
export ENABLE_PROJECT_NAME="test-project"
412+
413+
run enable_ralph_in_directory
414+
415+
assert_success
416+
[[ -f ".gitignore" ]]
417+
grep -q ".ralph/.call_count" .gitignore
418+
}
419+
420+
@test "enable_ralph_in_directory skips .gitignore when one exists and no force" {
421+
mkdir -p "$HOME/.ralph/templates"
422+
echo ".ralph/.call_count" > "$HOME/.ralph/templates/.gitignore"
423+
424+
# Pre-existing .gitignore
425+
echo "my-custom-ignore" > .gitignore
426+
427+
export ENABLE_FORCE="false"
428+
export ENABLE_SKIP_TASKS="true"
429+
export ENABLE_PROJECT_NAME="test-project"
430+
431+
run enable_ralph_in_directory
432+
433+
assert_success
434+
# Should preserve existing .gitignore content
435+
grep -q "my-custom-ignore" .gitignore
436+
}
437+
438+
@test "enable_ralph_in_directory overwrites .gitignore with force" {
439+
mkdir -p "$HOME/.ralph/templates"
440+
echo ".ralph/.call_count" > "$HOME/.ralph/templates/.gitignore"
441+
442+
# Pre-existing .gitignore with different content
443+
echo "my-custom-ignore" > .gitignore
444+
445+
export ENABLE_FORCE="true"
446+
export ENABLE_SKIP_TASKS="true"
447+
export ENABLE_PROJECT_NAME="test-project"
448+
449+
run enable_ralph_in_directory
450+
451+
assert_success
452+
# Should have template content, not old content
453+
grep -q ".ralph/.call_count" .gitignore
454+
! grep -q "my-custom-ignore" .gitignore
455+
}
456+
457+
@test "enable_ralph_in_directory succeeds when templates dir exists but .gitignore is missing" {
458+
# Templates dir exists but no .gitignore template inside
459+
mkdir -p "$HOME/.ralph/templates"
460+
461+
export ENABLE_FORCE="false"
462+
export ENABLE_SKIP_TASKS="true"
463+
export ENABLE_PROJECT_NAME="test-project"
464+
465+
run enable_ralph_in_directory
466+
467+
assert_success
468+
# .gitignore should not be created
469+
[[ ! -f ".gitignore" ]]
470+
}

0 commit comments

Comments
 (0)