Skip to content

Commit 3f046c9

Browse files
committed
+ finalize by claude
1 parent cb4c755 commit 3f046c9

5 files changed

Lines changed: 160 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44

55
## 2.0.0
66

7+
- Added Claude-delegation: ask AI to finalize the installation (if present)
78
- Add Claude Code CLI to the container (`dip claude`)
8-
- LSP support
9-
- Vite support
10-
- MySQL support
9+
- More configurations/features supported: LSPs, Vite, MySQL
1110

1211
## 1.1.0
1312

template/_claude_finalize.rb

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Check if Claude CLI is available and offer to run it for polishing the configuration
2+
3+
claude_available = system("which claude > /dev/null 2>&1")
4+
5+
if claude_available
6+
if yes?("Would you like to run Claude to review and polish your Docker configuration?")
7+
# Build the prompt with todos
8+
todos_text = todos.map { |t| t.sub(/^\s*-?\s*/, "- ") }.join("\n")
9+
10+
prompt = <<~PROMPT
11+
I just set up a Docker development environment for this Rails application using the "Ruby on Whales" template.
12+
13+
## Generated files
14+
15+
The following files were created in `#{DOCKER_DEV_ROOT}/`:
16+
- `Dockerfile` - Multi-stage Docker image for development
17+
- `compose.yml` - Docker Compose configuration
18+
- `Aptfile` - System dependencies
19+
- `README.md` - Usage documentation
20+
21+
And `dip.yml` in the project root for the Dip CLI.
22+
23+
## TODOs to complete
24+
25+
#{todos_text}
26+
27+
## Your tasks
28+
29+
1. **Review the generated configuration** - Check the files in `#{DOCKER_DEV_ROOT}/` and `dip.yml` to ensure they match the project's needs
30+
2. **Complete the TODOs** - Help me address each item listed above
31+
3. **Suggest improvements** - Based on this project's structure, suggest any additional Docker configuration that might be helpful
32+
4. **Document the setup** - Create or update documentation appropriate for this project:
33+
- If CLAUDE.md or AGENTS.md exists, add Docker-related instructions there
34+
- If there's a docs/ folder, consider adding a Docker setup guide
35+
- Otherwise, ensure #{DOCKER_DEV_ROOT}/README.md is comprehensive
36+
37+
## References
38+
39+
For more context, see these guides:
40+
- [Ruby on Whales: Dockerizing Ruby and Rails development](https://evilmartians.com/chronicles/ruby-on-whales-docker-for-ruby-rails-development) - The main guide this template is based on
41+
- [System of a test: Dockerizing system tests](https://evilmartians.com/chronicles/system-of-a-test-setting-up-end-to-end-rails-testing#dockerizing-system-tests) - For setting up system/integration tests with Docker
42+
- [Vite-lizing Rails: Dockerizing Vite](https://evilmartians.com/chronicles/vite-lizing-rails-get-live-reload-and-hot-replacement-with-vite-ruby#dockerizing-vite-or-not) - For Vite.js integration with Docker
43+
PROMPT
44+
45+
say_status :info, "Handing over to Claude to review your Docker setup...\n"
46+
47+
# Use fork + exec to hand over control to Claude for interactive session
48+
# while allowing the parent process to complete
49+
pid = fork do
50+
exec(
51+
"claude",
52+
"--allowedTools", "Read", "Edit", "Write", "MultiEdit", "Glob", "Grep", "Bash(git *)",
53+
prompt
54+
)
55+
end
56+
Process.wait(pid)
57+
end
58+
end

template/ruby-on-whales.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,8 @@
4848
say_status(:warn, todos.join("\n"))
4949
end
5050

51+
<%= include "claude_finalize" %>
52+
53+
54+
5155
say_status :info, "✅ You're ready to sail! Check out #{DOCKER_DEV_ROOT}/README.md or run `dip provision && dip up web` 🚀"

test/fixtures/bin/claude

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/bin/bash
2+
# Mock claude binary for testing
3+
# Writes all arguments to a file specified by CLAUDE_MOCK_OUTPUT env var
4+
5+
output_file="${CLAUDE_MOCK_OUTPUT:-/tmp/claude_mock_args.txt}"
6+
echo "$@" > "$output_file"
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# frozen_string_literal: true
2+
3+
require "test_helper"
4+
5+
class ClaudeFinalizeTest < GeneratorTestCase
6+
template <<~'CODE'
7+
DOCKER_DEV_ROOT = ".dockerdev"
8+
todos = [
9+
"📝 Important things to take care of:",
10+
" - Make sure you have `ENV[\"RAILS_ENV\"] = \"test\"` (not `ENV[\"RAILS_ENV\"] ||= \"test\"`) in your test helper.",
11+
" - Don't forget to add DATABASE_URL to your database.yml"
12+
]
13+
14+
<%= include "claude_finalize" %>
15+
CODE
16+
17+
def setup
18+
super
19+
@mock_bin = File.expand_path("../fixtures/bin", __dir__)
20+
@claude_output_file = File.join(TMP_DIR, "claude_args.txt")
21+
end
22+
23+
def test_runs_claude_with_correct_arguments
24+
original_path = ENV["PATH"]
25+
ENV["PATH"] = "#{@mock_bin}:#{original_path}"
26+
ENV["CLAUDE_MOCK_OUTPUT"] = @claude_output_file
27+
28+
begin
29+
run_generator(input: ["y"]) # "y" for yes? prompt
30+
ensure
31+
ENV["PATH"] = original_path
32+
ENV.delete("CLAUDE_MOCK_OUTPUT")
33+
end
34+
35+
assert File.exist?(@claude_output_file), "Mock claude should have been called"
36+
37+
args = File.read(@claude_output_file)
38+
39+
# Check --allowedTools flag and tools
40+
assert_includes args, "--allowedTools"
41+
assert_includes args, "Read"
42+
assert_includes args, "Edit"
43+
assert_includes args, "Write"
44+
assert_includes args, "Grep"
45+
assert_includes args, "Bash(git *)"
46+
47+
# Check prompt content includes todos
48+
assert_includes args, "Ruby on Whales"
49+
assert_includes args, "TODOs to complete"
50+
assert_includes args, "Important things to take care of"
51+
assert_includes args, "ENV[\"RAILS_ENV\"] = \"test\""
52+
assert_includes args, "DATABASE_URL"
53+
54+
# Check prompt includes references
55+
assert_includes args, "evilmartians.com/chronicles/ruby-on-whales-docker-for-ruby-rails-development"
56+
assert_includes args, "system-of-a-test"
57+
assert_includes args, "vite-lizing-rails"
58+
end
59+
60+
def test_skips_when_user_declines
61+
original_path = ENV["PATH"]
62+
ENV["PATH"] = "#{@mock_bin}:#{original_path}"
63+
ENV["CLAUDE_MOCK_OUTPUT"] = @claude_output_file
64+
65+
begin
66+
run_generator(input: ["n"]) # "n" for no
67+
ensure
68+
ENV["PATH"] = original_path
69+
ENV.delete("CLAUDE_MOCK_OUTPUT")
70+
end
71+
72+
refute File.exist?(@claude_output_file), "Mock claude should not have been called when user declines"
73+
end
74+
75+
def test_skips_when_claude_unavailable
76+
# Don't add mock bin to PATH, so claude won't be found
77+
ENV["CLAUDE_MOCK_OUTPUT"] = @claude_output_file
78+
original_path = ENV["PATH"]
79+
ENV["PATH"] = __dir__
80+
81+
begin
82+
run_generator(input: []) # No input needed since prompt won't appear
83+
ensure
84+
ENV["PATH"] = original_path
85+
ENV.delete("CLAUDE_MOCK_OUTPUT")
86+
end
87+
88+
refute File.exist?(@claude_output_file), "Mock claude should not have been called when unavailable"
89+
end
90+
end

0 commit comments

Comments
 (0)