rails_claude_skills 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.yml +134 -0
  3. data/.github/ISSUE_TEMPLATE/config.yml +11 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.yml +129 -0
  5. data/.github/ISSUE_TEMPLATE/question.yml +90 -0
  6. data/.github/dependabot.yml +19 -0
  7. data/.github/workflows/ci.yml +77 -0
  8. data/.github/workflows/release.yml +66 -0
  9. data/.rubocop.yml +52 -0
  10. data/CHANGELOG.md +94 -0
  11. data/CLAUDE.md +332 -0
  12. data/CODE_OF_CONDUCT.md +134 -0
  13. data/CONTRIBUTING.md +580 -0
  14. data/LICENSE.txt +21 -0
  15. data/README.md +544 -0
  16. data/Rakefile +8 -0
  17. data/lib/generators/claude/agent/agent_generator.rb +71 -0
  18. data/lib/generators/claude/agent/templates/agent.md.tt +62 -0
  19. data/lib/generators/claude/command/command_generator.rb +50 -0
  20. data/lib/generators/claude/command/templates/command.md.tt +28 -0
  21. data/lib/generators/claude/commands_library/create-pr.md +27 -0
  22. data/lib/generators/claude/commands_library/dbchange.md +19 -0
  23. data/lib/generators/claude/commands_library/quality.md +20 -0
  24. data/lib/generators/claude/commands_library/stimulus.md +19 -0
  25. data/lib/generators/claude/commands_library/turbo-feature.md +17 -0
  26. data/lib/generators/claude/install/install_generator.rb +211 -0
  27. data/lib/generators/claude/install/templates/README.md.tt +59 -0
  28. data/lib/generators/claude/install/templates/USAGE +28 -0
  29. data/lib/generators/claude/install/templates/agents/api-dev.md.tt +46 -0
  30. data/lib/generators/claude/install/templates/agents/fullstack-dev.md.tt +48 -0
  31. data/lib/generators/claude/install/templates/agents/rails-developer.md.tt +40 -0
  32. data/lib/generators/claude/install/templates/settings.local.json.tt +13 -0
  33. data/lib/generators/claude/rule/rule_generator.rb +175 -0
  34. data/lib/generators/claude/rule/templates/rule.md.tt +7 -0
  35. data/lib/generators/claude/rules_library/code-style.md +37 -0
  36. data/lib/generators/claude/rules_library/database.md +47 -0
  37. data/lib/generators/claude/rules_library/hotwire.md +56 -0
  38. data/lib/generators/claude/rules_library/security.md +54 -0
  39. data/lib/generators/claude/rules_library/testing.md +47 -0
  40. data/lib/generators/claude/skill/skill_generator.rb +196 -0
  41. data/lib/generators/claude/skill/templates/SKILL.md.tt +27 -0
  42. data/lib/generators/claude/skills_library/create-task-files/SKILL.md +311 -0
  43. data/lib/generators/claude/skills_library/create-task-files/templates/bug.md +60 -0
  44. data/lib/generators/claude/skills_library/create-task-files/templates/epic.md +47 -0
  45. data/lib/generators/claude/skills_library/create-task-files/templates/issue.md +45 -0
  46. data/lib/generators/claude/skills_library/create-task-files/templates/user-story.md +57 -0
  47. data/lib/generators/claude/skills_library/minitest-testing/SKILL.md +398 -0
  48. data/lib/generators/claude/skills_library/minitest-testing/references/examples.md +889 -0
  49. data/lib/generators/claude/skills_library/plan-feature/SKILL.md +253 -0
  50. data/lib/generators/claude/skills_library/rails-api-controllers/SKILL.md +1041 -0
  51. data/lib/generators/claude/skills_library/rails-api-controllers/references/api-documentation.md +422 -0
  52. data/lib/generators/claude/skills_library/rails-api-controllers/references/serialization.md +456 -0
  53. data/lib/generators/claude/skills_library/rails-auth-with-devise/SKILL.md +191 -0
  54. data/lib/generators/claude/skills_library/rails-auth-with-devise/references/advanced.md +331 -0
  55. data/lib/generators/claude/skills_library/rails-auth-with-devise/references/api-auth.md +266 -0
  56. data/lib/generators/claude/skills_library/rails-auth-with-devise/references/omniauth.md +194 -0
  57. data/lib/generators/claude/skills_library/rails-authorization-cancancan/SKILL.md +603 -0
  58. data/lib/generators/claude/skills_library/rails-authorization-cancancan/references/api-authorization.md +543 -0
  59. data/lib/generators/claude/skills_library/rails-authorization-cancancan/references/complex-permissions.md +572 -0
  60. data/lib/generators/claude/skills_library/rails-authorization-cancancan/references/multi-tenancy.md +373 -0
  61. data/lib/generators/claude/skills_library/rails-controllers/SKILL.md +514 -0
  62. data/lib/generators/claude/skills_library/rails-debugging/SKILL.md +260 -0
  63. data/lib/generators/claude/skills_library/rails-deployment/SKILL.md +437 -0
  64. data/lib/generators/claude/skills_library/rails-deployment/references/examples.md +901 -0
  65. data/lib/generators/claude/skills_library/rails-hotwire/SKILL.md +367 -0
  66. data/lib/generators/claude/skills_library/rails-jobs/MISSION_CONTROL_SETUP.md +639 -0
  67. data/lib/generators/claude/skills_library/rails-jobs/SKILL.md +704 -0
  68. data/lib/generators/claude/skills_library/rails-mailers/SKILL.md +549 -0
  69. data/lib/generators/claude/skills_library/rails-models/SKILL.md +379 -0
  70. data/lib/generators/claude/skills_library/rails-pagination-kaminari/SKILL.md +622 -0
  71. data/lib/generators/claude/skills_library/rails-pagination-kaminari/references/api-pagination.md +523 -0
  72. data/lib/generators/claude/skills_library/rails-pagination-kaminari/references/custom-themes.md +498 -0
  73. data/lib/generators/claude/skills_library/rails-pagination-kaminari/references/performance.md +478 -0
  74. data/lib/generators/claude/skills_library/rails-views/SKILL.md +508 -0
  75. data/lib/generators/claude/skills_library/refine-requirements/SKILL.md +226 -0
  76. data/lib/generators/claude/skills_library/refine-requirements/references/examples.md +344 -0
  77. data/lib/generators/claude/skills_library/refine-requirements/references/reference.md +298 -0
  78. data/lib/generators/claude/skills_library/rspec-testing/SKILL.md +572 -0
  79. data/lib/generators/claude/skills_library/rspec-testing/references/better_specs_guide.md +273 -0
  80. data/lib/generators/claude/skills_library/rspec-testing/references/thoughtbot_patterns.md +407 -0
  81. data/lib/generators/claude/skills_library/tailwindcss/SKILL.md +371 -0
  82. data/lib/generators/claude/views/views_generator.rb +113 -0
  83. data/lib/rails_claude_skills/railtie.rb +16 -0
  84. data/lib/rails_claude_skills/version.rb +5 -0
  85. data/lib/rails_claude_skills.rb +27 -0
  86. data/sig/rails_claude_skills.rbs +4 -0
  87. metadata +199 -0
@@ -0,0 +1,901 @@
1
+ ## Rails Deployment Examples
2
+
3
+ Complete deployment patterns and examples for production Rails applications with Kamal.
4
+
5
+ ---
6
+
7
+ ## Kamal Configuration
8
+
9
+ ### Basic Single-Server Setup
10
+
11
+ ```yaml
12
+ # config/deploy.yml
13
+ service: my-blog-app
14
+ image: username/my-blog-app
15
+
16
+ servers:
17
+ web:
18
+ hosts:
19
+ - 192.168.1.100
20
+ labels:
21
+ traefik.http.routers.my-blog-app.rule: Host(`blog.example.com`)
22
+ traefik.http.routers.my-blog-app-secure.entrypoints: websecure
23
+ traefik.http.routers.my-blog-app-secure.rule: Host(`blog.example.com`)
24
+ traefik.http.routers.my-blog-app-secure.tls: true
25
+ traefik.http.routers.my-blog-app-secure.tls.certresolver: letsencrypt
26
+
27
+ registry:
28
+ username: username
29
+ password:
30
+ - KAMAL_REGISTRY_PASSWORD
31
+
32
+ env:
33
+ secret:
34
+ - RAILS_MASTER_KEY
35
+ clear:
36
+ RAILS_ENV: production
37
+ RAILS_LOG_TO_STDOUT: enabled
38
+ RAILS_SERVE_STATIC_FILES: enabled
39
+
40
+ builder:
41
+ arch: amd64
42
+ remote:
43
+ host: 192.168.1.100
44
+
45
+ traefik:
46
+ options:
47
+ publish:
48
+ - 443:443
49
+ - 80:80
50
+ volume:
51
+ - "/letsencrypt:/letsencrypt"
52
+ args:
53
+ entryPoints.web.address: ":80"
54
+ entryPoints.websecure.address: ":443"
55
+ certificatesResolvers.letsencrypt.acme.email: "admin@example.com"
56
+ certificatesResolvers.letsencrypt.acme.storage: "/letsencrypt/acme.json"
57
+ certificatesResolvers.letsencrypt.acme.httpchallenge: true
58
+ certificatesResolvers.letsencrypt.acme.httpchallenge.entrypoint: web
59
+
60
+ healthcheck:
61
+ path: /up
62
+ port: 3000
63
+ max_attempts: 10
64
+ interval: 10s
65
+ ```
66
+
67
+ ### Multi-Server Setup with Load Balancing
68
+
69
+ ```yaml
70
+ # config/deploy.yml
71
+ service: my-app
72
+ image: username/my-app
73
+
74
+ servers:
75
+ web:
76
+ hosts:
77
+ - 192.168.1.101
78
+ - 192.168.1.102
79
+ - 192.168.1.103
80
+ labels:
81
+ traefik.http.routers.my-app.rule: Host(`app.example.com`)
82
+ traefik.http.routers.my-app-secure.entrypoints: websecure
83
+ traefik.http.routers.my-app-secure.rule: Host(`app.example.com`)
84
+ traefik.http.routers.my-app-secure.tls: true
85
+ traefik.http.routers.my-app-secure.tls.certresolver: letsencrypt
86
+ options:
87
+ network: "private"
88
+
89
+ # Separate worker servers for background jobs
90
+ workers:
91
+ hosts:
92
+ - 192.168.1.104
93
+ - 192.168.1.105
94
+ cmd: bundle exec rake solid_queue:start
95
+ options:
96
+ network: "private"
97
+
98
+ registry:
99
+ username: username
100
+ password:
101
+ - KAMAL_REGISTRY_PASSWORD
102
+
103
+ env:
104
+ secret:
105
+ - RAILS_MASTER_KEY
106
+ - DATABASE_URL
107
+ clear:
108
+ RAILS_ENV: production
109
+ RAILS_LOG_TO_STDOUT: enabled
110
+ WEB_CONCURRENCY: 3
111
+ RAILS_MAX_THREADS: 5
112
+
113
+ builder:
114
+ arch: amd64
115
+
116
+ traefik:
117
+ host_port: 192.168.1.100 # Dedicated load balancer
118
+ options:
119
+ publish:
120
+ - 443:443
121
+ - 80:80
122
+ volume:
123
+ - "/letsencrypt:/letsencrypt"
124
+ network: "private"
125
+ args:
126
+ entryPoints.web.address: ":80"
127
+ entryPoints.websecure.address: ":443"
128
+ certificatesResolvers.letsencrypt.acme.email: "admin@example.com"
129
+ certificatesResolvers.letsencrypt.acme.storage: "/letsencrypt/acme.json"
130
+ certificatesResolvers.letsencrypt.acme.httpchallenge: true
131
+ certificatesResolvers.letsencrypt.acme.httpchallenge.entrypoint: web
132
+
133
+ healthcheck:
134
+ path: /up
135
+ port: 3000
136
+ max_attempts: 10
137
+ interval: 10s
138
+ ```
139
+
140
+ ### Staging + Production Environments
141
+
142
+ ```yaml
143
+ # config/deploy.yml (production)
144
+ service: my-app-production
145
+ image: username/my-app
146
+
147
+ servers:
148
+ web:
149
+ hosts:
150
+ - production.example.com
151
+ labels:
152
+ traefik.http.routers.my-app-prod.rule: Host(`app.example.com`)
153
+
154
+ env:
155
+ secret:
156
+ - RAILS_MASTER_KEY
157
+ clear:
158
+ RAILS_ENV: production
159
+ ```
160
+
161
+ ```yaml
162
+ # config/deploy.staging.yml (staging)
163
+ service: my-app-staging
164
+ image: username/my-app
165
+
166
+ servers:
167
+ web:
168
+ hosts:
169
+ - staging.example.com
170
+ labels:
171
+ traefik.http.routers.my-app-staging.rule: Host(`staging.app.example.com`)
172
+
173
+ env:
174
+ secret:
175
+ - RAILS_MASTER_KEY
176
+ clear:
177
+ RAILS_ENV: staging
178
+ ```
179
+
180
+ ```bash
181
+ # Deploy to staging
182
+ kamal deploy -c config/deploy.staging.yml
183
+
184
+ # Deploy to production
185
+ kamal deploy -c config/deploy.yml
186
+ ```
187
+
188
+ ---
189
+
190
+ ## Docker Configuration
191
+
192
+ ### Optimized Multi-Stage Dockerfile
193
+
194
+ ```dockerfile
195
+ # syntax = docker/dockerfile:1
196
+
197
+ ARG RUBY_VERSION=3.4.7
198
+ FROM ruby:$RUBY_VERSION-slim AS base
199
+
200
+ # Set working directory
201
+ WORKDIR /rails
202
+
203
+ # Install base packages
204
+ RUN apt-get update -qq && \
205
+ apt-get install --no-install-recommends -y \
206
+ curl \
207
+ libjemalloc2 \
208
+ libvips \
209
+ sqlite3 && \
210
+ rm -rf /var/lib/apt/lists /var/cache/apt/archives
211
+
212
+ # Set production environment
213
+ ENV RAILS_ENV="production" \
214
+ BUNDLE_DEPLOYMENT="1" \
215
+ BUNDLE_PATH="/usr/local/bundle" \
216
+ BUNDLE_WITHOUT="development:test" \
217
+ MALLOC_CONF="background_thread:true,metadata_thp:auto,dirty_decay_ms:5000,muzzy_decay_ms:5000"
218
+
219
+ # Build stage
220
+ FROM base AS build
221
+
222
+ # Install build packages
223
+ RUN apt-get update -qq && \
224
+ apt-get install --no-install-recommends -y \
225
+ build-essential \
226
+ git \
227
+ pkg-config && \
228
+ rm -rf /var/lib/apt/lists /var/cache/apt/archives
229
+
230
+ # Install gems
231
+ COPY Gemfile Gemfile.lock ./
232
+ RUN bundle install && \
233
+ rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \
234
+ bundle exec bootsnap precompile --gemfile
235
+
236
+ # Copy application code
237
+ COPY . .
238
+
239
+ # Precompile bootsnap code for faster boot times
240
+ RUN bundle exec bootsnap precompile app/ lib/
241
+
242
+ # Precompile assets
243
+ RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile
244
+
245
+ # Final stage
246
+ FROM base
247
+
248
+ # Copy built artifacts
249
+ COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}"
250
+ COPY --from=build /rails /rails
251
+
252
+ # Create user
253
+ RUN groupadd --system --gid 1000 rails && \
254
+ useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \
255
+ chown -R rails:rails db log storage tmp
256
+ USER 1000:1000
257
+
258
+ # Entrypoint
259
+ ENTRYPOINT ["/rails/bin/docker-entrypoint"]
260
+
261
+ EXPOSE 3000
262
+ CMD ["./bin/thrust", "./bin/rails", "server"]
263
+ ```
264
+
265
+ ### Docker Entrypoint Script
266
+
267
+ ```bash
268
+ #!/bin/bash -e
269
+ # bin/docker-entrypoint
270
+
271
+ # Remove pre-existing server.pid
272
+ rm -f /rails/tmp/pids/server.pid
273
+
274
+ # Run migrations if DATABASE_URL is set
275
+ if [ -n "$DATABASE_URL" ]; then
276
+ echo "Running database migrations..."
277
+ bundle exec rails db:prepare
278
+ fi
279
+
280
+ # Execute CMD
281
+ exec "${@}"
282
+ ```
283
+
284
+ ```bash
285
+ # Make executable
286
+ chmod +x bin/docker-entrypoint
287
+ ```
288
+
289
+ ### .dockerignore
290
+
291
+ ```
292
+ # .dockerignore
293
+ .git
294
+ .github
295
+ .gitignore
296
+ .dockerignore
297
+
298
+ # Development/Test
299
+ .env*
300
+ !.env.example
301
+ coverage/
302
+ log/*
303
+ tmp/*
304
+ *.log
305
+ *.pid
306
+
307
+ # Documentation
308
+ README.md
309
+ CHANGELOG.md
310
+ doc/
311
+
312
+ # Dependencies
313
+ node_modules/
314
+ vendor/bundle/
315
+
316
+ # Build artifacts
317
+ public/assets/
318
+ public/packs/
319
+
320
+ # IDE
321
+ .idea/
322
+ .vscode/
323
+ *.swp
324
+ *.swo
325
+ ```
326
+
327
+ ---
328
+
329
+ ## Environment Secrets
330
+
331
+ ### Using Rails Credentials
332
+
333
+ ```bash
334
+ # Edit production credentials
335
+ EDITOR=nano rails credentials:edit --environment production
336
+
337
+ # Save to config/credentials/production.key (keep secure, don't commit)
338
+ ```
339
+
340
+ ```yaml
341
+ # config/credentials/production.yml.enc (encrypted, safe to commit)
342
+ secret_key_base: abc123...
343
+
344
+ database:
345
+ host: db.example.com
346
+ username: myapp_prod
347
+ password: secure_password
348
+
349
+ smtp:
350
+ address: smtp.sendgrid.net
351
+ username: apikey
352
+ password: SG.xxx
353
+
354
+ aws:
355
+ access_key_id: AKIA...
356
+ secret_access_key: xxx
357
+
358
+ stripe:
359
+ publishable_key: pk_live_...
360
+ secret_key: sk_live_...
361
+ ```
362
+
363
+ ```ruby
364
+ # config/database.yml
365
+ production:
366
+ <<: *default
367
+ host: <%= Rails.application.credentials.dig(:database, :host) %>
368
+ database: <%= ENV.fetch("DATABASE_NAME", "myapp_production") %>
369
+ username: <%= Rails.application.credentials.dig(:database, :username) %>
370
+ password: <%= Rails.application.credentials.dig(:database, :password) %>
371
+ ```
372
+
373
+ ### Using Environment Variables
374
+
375
+ ```ruby
376
+ # .env (local only, not committed)
377
+ KAMAL_REGISTRY_PASSWORD=docker_hub_password
378
+ RAILS_MASTER_KEY=abc123...
379
+ DATABASE_URL=postgresql://user:pass@host:5432/dbname
380
+ SMTP_USERNAME=apikey
381
+ SMTP_PASSWORD=sendgrid_key
382
+ ```
383
+
384
+ ```yaml
385
+ # config/deploy.yml
386
+ env:
387
+ secret:
388
+ - RAILS_MASTER_KEY
389
+ - DATABASE_URL
390
+ - SMTP_USERNAME
391
+ - SMTP_PASSWORD
392
+ clear:
393
+ RAILS_ENV: production
394
+ RAILS_LOG_TO_STDOUT: enabled
395
+ RAILS_SERVE_STATIC_FILES: enabled
396
+ ```
397
+
398
+ ---
399
+
400
+ ## Database Deployment
401
+
402
+ ### PostgreSQL with External Database
403
+
404
+ ```yaml
405
+ # config/deploy.yml
406
+ env:
407
+ secret:
408
+ - DATABASE_URL # postgresql://user:pass@host:5432/dbname
409
+ ```
410
+
411
+ ```ruby
412
+ # config/database.yml
413
+ production:
414
+ <<: *default
415
+ url: <%= ENV["DATABASE_URL"] %>
416
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS", 5) %>
417
+ ```
418
+
419
+ ### SQLite with Docker Volumes
420
+
421
+ ```yaml
422
+ # config/deploy.yml
423
+ volumes:
424
+ - "/var/lib/myapp/storage:/rails/storage"
425
+ - "/var/lib/myapp/db:/rails/db"
426
+ ```
427
+
428
+ ```ruby
429
+ # config/database.yml
430
+ production:
431
+ <<: *default
432
+ database: db/production.sqlite3
433
+ ```
434
+
435
+ ### Safe Migration Strategy
436
+
437
+ ```bash
438
+ # 1. Deploy code without pushing (uses existing image)
439
+ kamal deploy --skip-push
440
+
441
+ # 2. Check migration status
442
+ kamal app exec "bin/rails db:migrate:status"
443
+
444
+ # 3. Run migrations
445
+ kamal app exec "bin/rails db:migrate"
446
+
447
+ # 4. Verify success
448
+ kamal app exec "bin/rails db:version"
449
+
450
+ # 5. If issues, rollback migration
451
+ kamal app exec "bin/rails db:rollback"
452
+ ```
453
+
454
+ ### Pre-Deployment Migration Hook
455
+
456
+ ```yaml
457
+ # config/deploy.yml
458
+ hooks:
459
+ pre-deploy:
460
+ - path: config/deploy/hooks/pre-deploy.sh
461
+ on_error: abort
462
+ ```
463
+
464
+ ```bash
465
+ # config/deploy/hooks/pre-deploy.sh
466
+ #!/bin/bash
467
+ set -e
468
+
469
+ echo "Running database migrations..."
470
+ kamal app exec "bin/rails db:migrate"
471
+
472
+ echo "Migrations complete!"
473
+ ```
474
+
475
+ ---
476
+
477
+ ## SSL/TLS Configuration
478
+
479
+ ### Let's Encrypt (Automatic)
480
+
481
+ ```yaml
482
+ # config/deploy.yml
483
+ traefik:
484
+ options:
485
+ publish:
486
+ - 443:443
487
+ - 80:80
488
+ volume:
489
+ - "/letsencrypt:/letsencrypt"
490
+ args:
491
+ entryPoints.web.address: ":80"
492
+ entryPoints.web.http.redirections.entryPoint.to: websecure
493
+ entryPoints.web.http.redirections.entryPoint.scheme: https
494
+ entryPoints.websecure.address: ":443"
495
+ certificatesResolvers.letsencrypt.acme.email: "admin@example.com"
496
+ certificatesResolvers.letsencrypt.acme.storage: "/letsencrypt/acme.json"
497
+ certificatesResolvers.letsencrypt.acme.httpchallenge: true
498
+ certificatesResolvers.letsencrypt.acme.httpchallenge.entrypoint: web
499
+
500
+ servers:
501
+ web:
502
+ labels:
503
+ traefik.http.routers.myapp-secure.tls.certresolver: letsencrypt
504
+ ```
505
+
506
+ ### Custom SSL Certificate
507
+
508
+ ```yaml
509
+ # config/deploy.yml
510
+ traefik:
511
+ options:
512
+ publish:
513
+ - 443:443
514
+ - 80:80
515
+ volume:
516
+ - "/etc/ssl/certs:/certs:ro"
517
+ args:
518
+ entryPoints.web.address: ":80"
519
+ entryPoints.websecure.address: ":443"
520
+ providers.file.filename: /etc/traefik/tls.yml
521
+
522
+ # Upload certificates to server
523
+ # /etc/traefik/tls.yml:
524
+ # tls:
525
+ # certificates:
526
+ # - certFile: /certs/example.com.crt
527
+ # keyFile: /certs/example.com.key
528
+ ```
529
+
530
+ ---
531
+
532
+ ## Health Checks
533
+
534
+ ### Health Check Endpoint
535
+
536
+ ```ruby
537
+ # config/routes.rb
538
+ Rails.application.routes.draw do
539
+ get "up", to: "health#show", as: :health_check
540
+ end
541
+
542
+ # app/controllers/health_controller.rb
543
+ class HealthController < ApplicationController
544
+ skip_before_action :authenticate_user!
545
+
546
+ def show
547
+ # Check database connectivity
548
+ ActiveRecord::Base.connection.execute("SELECT 1")
549
+
550
+ # Check Solid Queue
551
+ SolidQueue::Job.count
552
+
553
+ # All checks passed
554
+ render json: {
555
+ status: "ok",
556
+ timestamp: Time.current,
557
+ version: ENV["APP_VERSION"]
558
+ }, status: :ok
559
+ rescue StandardError => e
560
+ render json: {
561
+ status: "error",
562
+ error: e.message
563
+ }, status: :service_unavailable
564
+ end
565
+ end
566
+ ```
567
+
568
+ ```yaml
569
+ # config/deploy.yml
570
+ healthcheck:
571
+ path: /up
572
+ port: 3000
573
+ max_attempts: 10
574
+ interval: 10s
575
+ timeout: 5s
576
+ ```
577
+
578
+ ---
579
+
580
+ ## Background Jobs Deployment
581
+
582
+ ### Solid Queue Workers
583
+
584
+ ```yaml
585
+ # config/deploy.yml
586
+ servers:
587
+ web:
588
+ hosts:
589
+ - web1.example.com
590
+ - web2.example.com
591
+
592
+ workers:
593
+ hosts:
594
+ - worker1.example.com
595
+ - worker2.example.com
596
+ cmd: bundle exec rake solid_queue:start
597
+ options:
598
+ network: "private"
599
+ ```
600
+
601
+ ```ruby
602
+ # config/recurring.yml (Solid Queue recurring tasks)
603
+ production:
604
+ cleanup_old_jobs:
605
+ class: CleanupJob
606
+ schedule: "0 2 * * *" # Daily at 2 AM
607
+ queue: maintenance
608
+
609
+ send_daily_digest:
610
+ class: DailyDigestJob
611
+ schedule: "0 8 * * *" # Daily at 8 AM
612
+ queue: mailers
613
+ ```
614
+
615
+ ---
616
+
617
+ ## Rollback Procedures
618
+
619
+ ### Rolling Back Deployment
620
+
621
+ ```bash
622
+ # List deployed versions
623
+ kamal app version
624
+
625
+ # Rollback to previous version
626
+ kamal rollback <VERSION>
627
+
628
+ # Example
629
+ kamal rollback 20240115123456
630
+ ```
631
+
632
+ ### Database Rollback
633
+
634
+ ```bash
635
+ # Check migration status
636
+ kamal app exec "bin/rails db:migrate:status"
637
+
638
+ # Rollback last migration
639
+ kamal app exec "bin/rails db:rollback"
640
+
641
+ # Rollback multiple migrations
642
+ kamal app exec "bin/rails db:rollback STEP=3"
643
+
644
+ # Rollback to specific version
645
+ kamal app exec "bin/rails db:migrate:down VERSION=20240115123456"
646
+ ```
647
+
648
+ ---
649
+
650
+ ## Monitoring & Logging
651
+
652
+ ### Structured Logging with Lograge
653
+
654
+ ```ruby
655
+ # Gemfile
656
+ gem "lograge"
657
+
658
+ # config/environments/production.rb
659
+ Rails.application.configure do
660
+ # Use Lograge for structured logs
661
+ config.lograge.enabled = true
662
+ config.lograge.formatter = Lograge::Formatters::Json.new
663
+
664
+ config.lograge.custom_options = lambda do |event|
665
+ {
666
+ params: event.payload[:params].except("controller", "action"),
667
+ user_id: event.payload[:user_id],
668
+ ip: event.payload[:ip],
669
+ request_id: event.payload[:request_id]
670
+ }
671
+ end
672
+ end
673
+ ```
674
+
675
+ ### Viewing Logs
676
+
677
+ ```bash
678
+ # Tail logs from all web servers
679
+ kamal app logs -f
680
+
681
+ # Tail logs from specific server
682
+ kamal app logs -f --hosts 192.168.1.101
683
+
684
+ # View last 100 lines
685
+ kamal app logs --lines 100
686
+
687
+ # Filter logs
688
+ kamal app logs -f | grep ERROR
689
+ ```
690
+
691
+ ### Error Tracking with Sentry
692
+
693
+ ```ruby
694
+ # Gemfile
695
+ gem "sentry-ruby"
696
+ gem "sentry-rails"
697
+
698
+ # config/initializers/sentry.rb
699
+ Sentry.init do |config|
700
+ config.dsn = Rails.application.credentials.dig(:sentry, :dsn)
701
+ config.breadcrumbs_logger = [:active_support_logger, :http_logger]
702
+ config.traces_sample_rate = 0.1
703
+ config.environment = Rails.env
704
+ end
705
+ ```
706
+
707
+ ---
708
+
709
+ ## CI/CD with GitHub Actions
710
+
711
+ ### Automated Deployment Workflow
712
+
713
+ ```yaml
714
+ # .github/workflows/deploy.yml
715
+ name: Deploy
716
+
717
+ on:
718
+ push:
719
+ branches: [main]
720
+
721
+ jobs:
722
+ test:
723
+ runs-on: ubuntu-latest
724
+ steps:
725
+ - uses: actions/checkout@v4
726
+
727
+ - name: Set up Ruby
728
+ uses: ruby/setup-ruby@v1
729
+ with:
730
+ bundler-cache: true
731
+
732
+ - name: Run tests
733
+ run: |
734
+ bin/rails test
735
+ bin/rubocop
736
+
737
+ deploy:
738
+ needs: test
739
+ runs-on: ubuntu-latest
740
+ steps:
741
+ - uses: actions/checkout@v4
742
+
743
+ - name: Set up Ruby
744
+ uses: ruby/setup-ruby@v1
745
+ with:
746
+ bundler-cache: true
747
+
748
+ - name: Install Kamal
749
+ run: gem install kamal
750
+
751
+ - name: Setup SSH
752
+ uses: webfactory/ssh-agent@v0.8.0
753
+ with:
754
+ ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
755
+
756
+ - name: Deploy with Kamal
757
+ env:
758
+ KAMAL_REGISTRY_PASSWORD: ${{ secrets.KAMAL_REGISTRY_PASSWORD }}
759
+ RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
760
+ run: |
761
+ kamal deploy
762
+ ```
763
+
764
+ ### Manual Deployment Workflow
765
+
766
+ ```yaml
767
+ # .github/workflows/manual-deploy.yml
768
+ name: Manual Deploy
769
+
770
+ on:
771
+ workflow_dispatch:
772
+ inputs:
773
+ environment:
774
+ description: "Environment to deploy"
775
+ required: true
776
+ type: choice
777
+ options:
778
+ - staging
779
+ - production
780
+
781
+ jobs:
782
+ deploy:
783
+ runs-on: ubuntu-latest
784
+ steps:
785
+ - uses: actions/checkout@v4
786
+
787
+ - name: Deploy to ${{ inputs.environment }}
788
+ run: |
789
+ if [ "${{ inputs.environment }}" = "staging" ]; then
790
+ kamal deploy -c config/deploy.staging.yml
791
+ else
792
+ kamal deploy -c config/deploy.yml
793
+ fi
794
+ ```
795
+
796
+ ---
797
+
798
+ ## Performance Optimization
799
+
800
+ ### Docker Image Optimization
801
+
802
+ ```dockerfile
803
+ # Use specific Ruby version
804
+ ARG RUBY_VERSION=3.4.7
805
+ FROM ruby:$RUBY_VERSION-slim
806
+
807
+ # Reduce layer count with multi-command RUN
808
+ RUN apt-get update -qq && \
809
+ apt-get install --no-install-recommends -y pkg1 pkg2 && \
810
+ rm -rf /var/lib/apt/lists /var/cache/apt/archives
811
+
812
+ # Copy only what's needed
813
+ COPY Gemfile Gemfile.lock ./
814
+ RUN bundle install
815
+
816
+ # Use .dockerignore to exclude unnecessary files
817
+ ```
818
+
819
+ ### Production Configuration
820
+
821
+ ```ruby
822
+ # config/environments/production.rb
823
+ Rails.application.configure do
824
+ # Serve assets via CDN
825
+ config.asset_host = ENV["CDN_HOST"]
826
+
827
+ # Enable caching
828
+ config.cache_classes = true
829
+ config.eager_load = true
830
+ config.consider_all_requests_local = false
831
+ config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present?
832
+
833
+ # Compress CSS
834
+ config.assets.css_compressor = :sass
835
+
836
+ # Enable Rack::Deflate for gzip compression
837
+ config.middleware.use Rack::Deflate
838
+
839
+ # Use SolidCache
840
+ config.cache_store = :solid_cache_store
841
+
842
+ # Active Job queue adapter
843
+ config.active_job.queue_adapter = :solid_queue
844
+
845
+ # Logging
846
+ config.log_level = :info
847
+ config.log_tags = [:request_id]
848
+ end
849
+ ```
850
+
851
+ ### Resource Limits
852
+
853
+ ```yaml
854
+ # config/deploy.yml
855
+ env:
856
+ clear:
857
+ WEB_CONCURRENCY: 3 # Number of Puma workers
858
+ RAILS_MAX_THREADS: 5 # Threads per worker
859
+ MALLOC_ARENA_MAX: 2 # Reduce memory fragmentation
860
+ ```
861
+
862
+ ---
863
+
864
+ ## Troubleshooting
865
+
866
+ ### Common Deployment Issues
867
+
868
+ ```bash
869
+ # Container not starting
870
+ kamal app logs -f
871
+ kamal app containers
872
+
873
+ # Check server resources
874
+ kamal app exec "free -h"
875
+ kamal app exec "df -h"
876
+
877
+ # Database connection issues
878
+ kamal app exec "bin/rails db:version"
879
+ kamal app exec "bin/rails console"
880
+
881
+ # Restart services
882
+ kamal app restart
883
+ kamal traefik restart
884
+
885
+ # Complete cleanup and redeploy
886
+ kamal app remove
887
+ kamal deploy
888
+ ```
889
+
890
+ ### Debug Mode
891
+
892
+ ```bash
893
+ # Enable verbose logging
894
+ kamal deploy --verbose
895
+
896
+ # SSH into container
897
+ kamal app exec --interactive bash
898
+
899
+ # Check environment variables
900
+ kamal app exec "env | grep RAILS"
901
+ ```