diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..2461085 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,166 @@ +--- +description: 'L4D Tools - Rails 8.1 application with Solid Queue, Hotwire stack' +applyTo: '**/*.rb' +--- + +# L4D Tools - AI Coding Guide + +## Project Overview + +**l4d_tools** is a Rails 8.1 application using modern Rails conventions. Key architectural decisions: +- **Database**: SQLite (development), supports production deployment via Kamal/Docker +- **Job Processing**: Solid Queue (replaces Sidekiq) - background jobs embedded with app via `:solid_queue` plugin in Puma +- **Frontend Stack**: Hotwire (Turbo + Stimulus) with importmap-rails and Propshaft +- **Testing**: Minitest with Capybara for system tests (parallelized workers) +- **Deployment**: Kamal-based Docker containerization with private registry support +- **Security Scanning**: Brakeman (security), Rubocop-rails-omakase (style), Bundler-audit (gems) + +## Critical Developer Workflows + +### Initial Setup +```bash +bin/setup # Installs gems, prepares DB, starts server +bin/setup --reset # Full reset including DB wipe +bin/setup --skip-server # Setup without starting server +``` + +### Development Commands +```bash +bin/dev # Start Rails server (port 3000) +bin/rails console # REPL with app context +bin/rails test # Full test suite (parallel workers) +bin/rails test test/path # Specific test file/directory +bundle exec brakeman # Security vulnerability scan +bundle exec rubocop -a # Auto-fix style violations +``` + +### Database Management +```bash +bin/rails db:prepare # Create/migrate DB +bin/rails db:reset # Wipe and re-seed +bin/rails db:migrate # Apply pending migrations +bin/rails db:rollback # Revert last migration +``` + +### Deployment (Kamal/Docker) +- **Config**: `config/deploy.yml` - registry, servers, environment variables +- **Secrets**: `.kamal/secrets` file (contains RAILS_MASTER_KEY) +- **Registry**: Private registry at localhost:5555 for deployment target +- **Job Processing**: `SOLID_QUEUE_IN_PUMA=true` runs supervisor in web process (single-server only) +- **Build**: `docker build -t l4d_tools .` builds production image + +## Architecture & Key Components + +### Job Processing (Solid Queue) +- **Location**: `app/jobs/` +- **Pattern**: Inherit from `ApplicationJob < ActiveJob::Base` +- **Configuration**: `config/queue.yml` defines adapters and queues +- **Execution**: Jobs run synchronously in test mode; async in development/production +- **Puma Integration**: `plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"]` in `config/puma.rb` +- **Usage**: `MyJob.perform_later(args)` for async; `MyJob.perform_now(args)` for testing + +### Frontend (Hotwire) +- **Turbo**: Rapid page transitions via AJAX without full reloads +- **Stimulus**: Minimal DOM binding framework; controllers in `app/javascript/controllers/` +- **Asset Pipeline**: Propshaft + importmap-rails; no build step needed +- **Entry Point**: `app/javascript/application.js` loads all controllers +- **Naming**: `data-controller="hello"` maps to `HelloController` in `app/javascript/controllers/hello_controller.js` + +### Database & Models +- **Schema**: `db/schema.rb` auto-generated from migrations (commit to VCS) +- **Migrations**: Generate via `bin/rails generate migration CreateTableName` +- **Patterns**: Use model scopes for complex queries; keep migrations simple and reversible +- **Fixtures**: YAML files in `test/fixtures/*.yml` auto-loaded by Minitest + +### Testing Structure +- **Unit Tests**: `test/models/`, `test/helpers/`, `test/controllers/` +- **Integration Tests**: `test/integration/` +- **System Tests**: Full browser tests via Capybara/Selenium +- **Parallelization**: Enabled by default; workers scale to CPU count +- **Fixtures**: Auto-loaded and available as lowercase underscore-separated variables (e.g., `posts(:one)`) + +## Project-Specific Guidelines + +- Follow RuboCop-rails-omakase style guide strictly; run `rubocop -a` to auto-fix +- Use snake_case for variables/methods, CamelCase for classes +- Extract reusable business logic into `app/services/` to keep models lean +- Use Solid Queue `perform_later` for background work; never block HTTP requests +- Prefer Turbo Frames for dynamic UI updates over full page reloads +- Write tests using fixtures; auto-loaded into test instance variables +- Use `bin/rails test` to run full suite with parallel workers +- Keep `config/routes.rb` simple; add routes only when adding controller actions +- Store secrets in `Rails.application.credentials` (encrypted via `master.key`) +- Debug with `byebug` or Rails logger; avoid `puts` for production debugging + +## Common Patterns + +### Job Enqueue (Solid Queue) +```ruby +# app/jobs/my_job.rb +class MyJob < ApplicationJob + queue_as :default + + def perform(arg1, arg2) + # async work here + end +end + +# Trigger: MyJob.perform_later(arg1, arg2) +# Test: MyJob.perform_now(arg1, arg2) +``` + +### Model Scopes & Queries +```ruby +# app/models/post.rb +class Post < ApplicationRecord + scope :published, -> { where(published: true) } + scope :recent, -> { order(created_at: :desc) } + + def self.active; where(active: true); end +end + +# Usage: Post.published.recent +``` + +### Stimulus Controller +```javascript +// app/javascript/controllers/search_controller.js +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + static targets = ["input"] + + search() { + console.log(this.inputTarget.value) + } +} +``` + +```erb + +
+ +
+``` + +### Test Pattern (Minitest) +```ruby +# test/models/post_test.rb +class PostTest < ActiveSupport::TestCase + fixtures :posts # Auto-loads test/fixtures/posts.yml + + test "post is valid" do + assert posts(:one).valid? + end +end +``` + +## Security & Production Readiness + +- Brakeman scans detect security issues; fix all warnings before merge +- Bundler-audit tracks vulnerable gems; update versions regularly +- Strong parameters required for all user input in controllers +- RAILS_MASTER_KEY (from `config/master.key`) must be set in Docker via secrets +- SQLite adequate for single-server; use PostgreSQL for multi-server scaling +- Content Security Policy configured in `config/initializers/content_security_policy.rb` +- Never commit `master.key` or `.kamal/secrets`; set RAILS_MASTER_KEY via environment