Skip to content

Instantly share code, notes, and snippets.

@cb341
Last active October 14, 2025 19:47
Show Gist options
  • Select an option

  • Save cb341/62543e2eb116d39bf5fbb5f1646f524c to your computer and use it in GitHub Desktop.

Select an option

Save cb341/62543e2eb116d39bf5fbb5f1646f524c to your computer and use it in GitHub Desktop.
SUPER COOL RAILS TEMPLATE
# frozen_string_literal: true
# -----------------------------------------------------------------------------
# Renuo Rails Application Template
# -----------------------------------------------------------------------------
# Purpose:
# Automates the setup of a new Rails project according to Renuo AG conventions.
#
# Features:
# * Enforces Ruby version consistency with `.ruby-version`
# * Configures development and code quality tools:
# - Renuocop (Rubocop configuration)
# - MarkdownLint (mdl)
# - Brakeman for security scanning
# * Sets up testing environment:
# - RSpec, SimpleCov, ParallelTests, FactoryBot
# - Enforces full coverage metrics using SimpleCov
# * Defines I18n configuration with default locale and fallbacks
# * Creates `/healthz` endpoint with controller, view, and request specs
# * Generates helper bin scripts:
# - `bin/setup` with 1Password secrets integration
# - `bin/check` and `bin/fastcheck` for validation and linting
# - `bin/bootstrap_github_repo` for Terraform-based GitHub setup
# * Cleans up Rails defaults (CSS skeleton, test directory)
# * Provides consistent project documentation and environment examples
#
# Usage:
# rails new APP_NAME --skip-ci --skip-camal -m path/to/template.rb
#
# Requirements:
# * Ruby, Bundler, Rails
# * Terraform
# * GitHub CLI (`gh`)
# -----------------------------------------------------------------------------
say_status :info, 'Applying renuo-rails template', :blue
# Ensure the Gemfile points to the Ruby version managed by .ruby-version
if File.read('Gemfile').match?(/^ruby/)
gsub_file 'Gemfile', /^ruby .*$/, "ruby file: '.ruby-version'\n"
else
insert_into_file 'Gemfile', "ruby file: '.ruby-version'\n", after: /^source.*\n/
end
# --------------------------------------------------------------------
# bin/setup
# --------------------------------------------------------------------
insert_into_file 'bin/setup', before: "\n puts \"\\n== Preparing database ==\"" do
<<-RUBY
puts "\\n== Fetching 1password dependencies =="
system! 'renuo fetch-secrets'#{' '}
RUBY
end
gsub_file 'bin/setup', /\n{0,2}[ \t]*unless ARGV.include\?\("--skip-server"\).*?end/m, ''
# --------------------------------------------------------------------
# Convenience scripts, checks
# --------------------------------------------------------------------
gem_group :development do
gem 'mdl', require: false
gem 'renuocop'
end
# Configure markdownlint
create_file '.mdlrc', <<~MDLRC, force: true
all
rule 'MD013', :line_length => 120
rule 'MD041', :enabled => false
MDLRC
# Ensure Rubocop uses Renuo defaults
create_file '.rubocop.yml', <<~YAML, force: true
inherit_gem:
renuocop: config/base.yml
YAML
create_file 'bin/run', <<~BASH, force: true
#!/usr/bin/env bash
set -euo pipefail
bundle exec rails server
BASH
create_file 'bin/fastcheck', <<~BASH, force: true
#!/usr/bin/env bash
set -euo pipefail
bundle exec brakeman -q -z --no-summary --no-pager
RUBOCOP_AUTO_CORRECT="-a"
if [[ "${1:-}" == "--dangerous" ]]; then
RUBOCOP_AUTO_CORRECT="-A"
fi
if ! bundle exec rubocop -D -c .rubocop.yml --fail-fast; then
bundle exec rubocop $RUBOCOP_AUTO_CORRECT -D -c .rubocop.yml
if ! bundle exec rubocop -D -c .rubocop.yml --fail-fast; then
echo 'Tried to auto correct the issues, but must be reviewed manually, commit aborted'
else
echo 'After formatting, everything looks good'
fi
exit 1
fi
bundle exec mdl README.md
BASH
create_file 'bin/check', <<~BASH, force: true
#!/usr/bin/env bash
set -euo pipefail
bin/rails zeitwerk:check
adapter=$(bundle exec rails runner 'puts ActiveRecord::Base.connection.adapter_name' 2>/dev/null | tr -d "
")
if [ "$adapter" != "SQLite" ] && [ "$adapter" != "SQLite3" ]; then
bundle exec rails parallel:drop >/dev/null 2>&1 || true
bundle exec rails parallel:setup
fi
NO_COVERAGE=true bundle exec parallel_rspec
bundle exec rspec
BASH
run 'chmod +x bin/fastcheck'
run 'chmod +x bin/check'
# --------------------------------------------------------------------
# Configure I18n
# --------------------------------------------------------------------
application <<~RUBY
config.i18n.available_locales = [:en]
config.i18n.default_locale = :en
config.i18n.fallbacks = true
RUBY
environment <<~RUBY, env: 'development'
config.i18n.raise_on_missing_translations = true
RUBY
environment <<~RUBY, env: 'test'
config.i18n.raise_on_missing_translations = true
RUBY
create_file 'config/locales/.keep'
create_file 'config/locales/en.yml', <<~YAML, force: true
en:
application:
name: "#{app_name.titleize}"
health:
title: "Application health"
ok: "Everything is running"
YAML
# --------------------------------------------------------------------
# Add rspec related gems
# --------------------------------------------------------------------
gem_group :development, :test do
gem 'factory_bot_rails'
gem 'parallel_tests'
end
gem_group :test do
gem 'super_diff'
gem 'rspec-rails'
gem 'simplecov', require: false
end
application <<~RUBY
config.generators do |g|
g.test_framework :rspec
g.system_tests = :rspec
end
RUBY
# --------------------------------------------------------------------
# Other configurations
# --------------------------------------------------------------------
gem 'simple_form'
schema_version = if defined?(ActiveRecord::VERSION)
"#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"
else
"#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}"
end
create_file 'db/schema.rb', <<~RUBY, force: true
# frozen_string_literal: true
ActiveRecord::Schema[#{schema_version}].define(version: 0) do
end
RUBY
# Append coverage folder to gitignore
append_to_file '.gitignore', <<~GITIGNORE unless File.read('.gitignore').include?('coverage')
# Coverage artifacts
coverage/
GITIGNORE
unless File.read('.gitignore').include?('.env')
append_to_file '.gitignore', "
.env
"
end
# Remove unused CSS skeleton to keep template unstyled
remove_dir 'app/assets/stylesheets' if Dir.exist?('app/assets/stylesheets')
empty_directory 'app/assets/stylesheets'
create_file 'app/assets/stylesheets/.keep'
# Add health check endpoint
route "get '/healthz', to: 'health#show'"
create_file 'app/controllers/health_controller.rb', <<~RUBY, force: true
class HealthController < ApplicationController
def show
checks = {
app: "ok"
}
respond_to do |format|
format.html
format.json { render json: { status: "ok", checks: checks }, status: :ok }
end
end
end
RUBY
create_file 'app/views/health/show.html.erb', <<~ERB, force: true
<main class="health">
<h1><%= t("health.title") %></h1>
<p><%= t("health.ok") %></p>
</main>
ERB
# Provide environment example for selenium driver
create_file '.env.example', <<~ENV, force: true
# Uncomment to run system specs with a visible browser
# SELENIUM_DRIVER="selenium_chrome"
SELENIUM_DRIVER="selenium_chrome_headless"
ENV
# GitHub Actions workflow for CI
create_file '.github/workflows/ci.yml', <<~YAML, force: true
name: CI
on:
push:
branches:
- main
- develop
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: .ruby-version
bundler-cache: true
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y xvfb libnss3
- name: Prepare database
run: bin/rails db:prepare
- name: Fast check
run: bin/fastcheck
- name: Run test suite
run: bin/check
YAML
# Documentation for generated app
create_file 'README.md', <<~MARKDOWN, force: true
# #{app_name.titleize}
Short project description
## Environments
| Branch | Domain | Deployment |
| ------- | ------------------------------------- | ---------------|
| develop | https://#{app_name}-develop.renuoapp.ch | auto |
| main | https://#{app_name}-main.renuoapp.ch | release |
## Setup
```sh
git clone git@github.com:renuo/#{app_name}.git
cd '#{app_name}'
bin/setup
```
### Run
```sh
bin/run
```
### Tests / Checks
```sh
bin/check
```
## Copyright
Copyright [Renuo AG](https://www.renuo.ch/).
MARKDOWN
after_bundle do
# --------------------------------------------------------------------
# Configure simple_form
# --------------------------------------------------------------------
generate 'simple_form:install'
gsub_file 'config/initializers/simple_form.rb',
'hint_class: :field_with_hint, error_class: :field_with_errors, valid_class: :field_without_errors do |b|',
"hint_class: :field_with_hint,\n error_class: :field_with_errors,\n valid_class: :field_without_errors do |b|"
# --------------------------------------------------------------------
# Configure rspec
# --------------------------------------------------------------------
remove_dir 'test'
generate 'rspec:install'
run 'bundle binstubs rspec-core'
create_file 'spec/spec_helper.rb', <<~'RUBY', force: true
# frozen_string_literal: true
require "simplecov" unless ENV["NO_COVERAGE"]
unless ENV["NO_COVERAGE"]
SimpleCov.start "rails" do
add_filter "app/channels/application_cable/channel.rb"
add_filter "app/channels/application_cable/connection.rb"
add_filter "app/jobs/application_job.rb"
add_filter "app/mailers/application_mailer.rb"
add_filter "app/models/application_record.rb"
enable_coverage :branch
minimum_coverage line: 100, branch: 100
end
test_env_number = ENV.fetch("TEST_ENV_NUMBER", "0")
SimpleCov.command_name "RSpec #{test_env_number}"
SimpleCov.use_merging true
end
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
config.shared_context_metadata_behavior = :apply_to_host_groups
config.run_all_when_everything_filtered = true
config.filter_run :focus
config.filter_run_excluding :skip
config.example_status_persistence_file_path = "tmp/rspec_examples.txt"
config.disable_monkey_patching!
config.default_formatter = "doc" if config.files_to_run.one?
config.profile_examples = 5
config.order = :random
Kernel.srand config.seed
config.define_derived_metadata do |metadata|
metadata[:aggregate_failures] = true
end
end
RUBY
create_file 'spec/rails_helper.rb', <<~'RUBY', force: true
# frozen_string_literal: true
ENV["RAILS_ENV"] ||= "test"
require "spec_helper"
require_relative "../config/environment"
abort("The Rails environment is running in production mode!") if Rails.env.production?
require "rspec/rails"
require "capybara/rspec"
require "capybara/rails"
require "selenium/webdriver"
require "super_diff/rspec-rails"
ActiveRecord::Migration.maintain_test_schema!
Rails.root.glob("spec/support/**/*.rb").each { |f| require f }
RSpec.configure do |config|
config.include FactoryBot::Syntax::Methods
config.include ActiveSupport::Testing::TimeHelpers
config.use_transactional_fixtures = true
config.infer_spec_type_from_file_location!
config.filter_rails_from_backtrace!
config.before do
ActionMailer::Base.deliveries.clear if defined?(ActionMailer::Base)
I18n.locale = I18n.default_locale
end
config.after do
Rails.logger.debug { "--- #{RSpec.current_example&.full_description} FINISHED ---" }
end
config.before(:each, :system) do
driven_by :rack_test
end
config.before(:all, :system) do
Capybara.server = :puma, { Silent: true }
end
config.before(:each, :system, :js) do
driven_by(ENV["SELENIUM_DRIVER"]&.to_sym || :selenium_chrome_headless)
Capybara.page.current_window.resize_to(1280, 800)
end
end
RUBY
create_file 'spec/support/factory_bot.rb', <<~'RUBY', force: true
# frozen_string_literal: true
RSpec.configure do |config|
config.include FactoryBot::Syntax::Methods
end
RUBY
create_file 'spec/support/javascript_error_reporter.rb', <<~'RUBY', force: true
# frozen_string_literal: true
module JavaScriptErrorReporter
RSpec.configure do |config|
config.after(:each, :system, :js) do
errors = page.driver.browser.logs.get(:browser)
aggregate_failures "javascript errors" do
errors.each do |error|
expect(error.level).not_to eq("SEVERE"), error.message
next unless error.level == "WARNING"
warn "\e[33m\nJAVASCRIPT WARNING\n#{error.message}\e[0m"
end
end
end
end
end
RUBY
create_file 'spec/support/capybara.rb', <<~'RUBY', force: true
# frozen_string_literal: true
require_relative "javascript_error_reporter"
require "fileutils"
require "tmpdir"
Capybara.register_driver :selenium_chrome do |app|
options = Selenium::WebDriver::Chrome::Options.new
options.add_argument("--window-size=1280,800")
profile_path = File.join(Dir.tmpdir, "selenium_profiles", ENV.fetch("TEST_ENV_NUMBER", "0"))
FileUtils.mkdir_p(profile_path)
options.add_argument("--user-data-dir=#{profile_path}")
Capybara::Selenium::Driver.new(app, browser: :chrome, options: options)
end
Capybara.register_driver :selenium_chrome_headless do |app|
options = Selenium::WebDriver::Chrome::Options.new
options.add_argument("--headless=new")
options.add_argument("--disable-gpu")
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--window-size=1280,800")
profile_path = File.join(Dir.tmpdir, "selenium_profiles", ENV.fetch("TEST_ENV_NUMBER", "0"))
FileUtils.mkdir_p(profile_path)
options.add_argument("--user-data-dir=#{profile_path}")
Capybara::Selenium::Driver.new(app, browser: :chrome, options: options)
end
RSpec.configure do |config|
config.include JavaScriptErrorReporter
end
Capybara.default_max_wait_time = 5
RUBY
create_file 'spec/requests/health_controller_spec.rb', <<~'RUBY', force: true
# frozen_string_literal: true
require "rails_helper"
RSpec.describe HealthController, type: :request do
it "returns a successful json response" do
get "/healthz", as: :json
expect(response).to have_http_status(:ok)
expect(response.parsed_body).to include("status" => "ok")
end
end
RUBY
create_file 'spec/system/health_spec.rb', <<~'RUBY', force: true
# frozen_string_literal: true
require "rails_helper"
RSpec.describe "Health check" do
it "shows the health page" do
visit "/healthz"
expect(page).to have_text(I18n.t("health.title"))
expect(page).to have_text(I18n.t("health.ok"))
end
it "supports the JSON endpoint", :js do
visit "/healthz.json"
expect(page).to have_text("status")
expect(page).to have_text("ok")
end
end
RUBY
# --------------------------------------------------------------------
# GitHub repository configuration
# --------------------------------------------------------------------
say_status :info, 'Setting up GitHub bootstrap scripts', :blue
bootstrap_dir = 'tmp/bootstrap_github_repo'
empty_directory bootstrap_dir
OWNER = 'CuddlyBunion341'
create_file "#{bootstrap_dir}/main.tf", <<~HCL
terraform {
required_providers {
github = {
source = "integrations/github"
version = "~> 6.5"
}
}
}
provider "github" {
owner = "#{OWNER}"
}
resource "github_repository" "repo" {
name = "#{app_name}"
description = "#{app_name.titleize} Rails Application"
visibility = "private"
has_issues = false
has_projects = false
has_wiki = false
}
resource "github_branch_protection" "develop" {
repository_id = github_repository.repo.node_id
pattern = "develop"
enforce_admins = true
required_status_checks {
strict = true
contexts = ["ci/semaphore/push"]
}
required_pull_request_reviews {
dismiss_stale_reviews = true
require_code_owner_reviews = false
}
required_linear_history = true
allows_deletions = false
}
resource "github_branch_protection" "main" {
repository_id = github_repository.repo.node_id
pattern = "main"
enforce_admins = true
required_pull_request_reviews {
dismiss_stale_reviews = false
require_code_owner_reviews = false
}
required_linear_history = true
allows_deletions = false
}
resource "github_repository_autolink_reference" "redmine" {
repository = github_repository.repo.name
key_prefix = "TICKET-"
target_url_template = "https://redmine.renuo.ch/issues/<num>"
}
HCL
create_file "#{bootstrap_dir}/run", <<~BASH
#!/usr/bin/env bash
set -euo pipefail
RED='\\033[0;31m'
GREEN='\\033[0;32m'
YELLOW='\\033[1;33m'
BLUE='\\033[0;34m'
NC='\\033[0m'
echo_info() { echo -e "${BLUE}==>${NC} $1"; }
echo_success() { echo -e "${GREEN}✓${NC} $1"; }
echo_error() { echo -e "${RED}✗${NC} $1" >&2; }
echo_warning() { echo -e "${YELLOW}!${NC} $1"; }
echo_info "Checking prerequisites..."
if ! command -v gh >/dev/null 2>&1; then
echo_error "GitHub CLI (gh) not found. Install from https://cli.github.com/"
exit 1
fi
echo_success "GitHub CLI found"
if ! gh auth status >/dev/null 2>&1; then
echo_error "Not authenticated with GitHub CLI. Run: gh auth login"
exit 1
fi
echo_success "GitHub CLI authenticated"
if ! command -v terraform >/dev/null 2>&1; then
echo_error "Terraform not found."
echo_warning "Install with Homebrew: brew install terraform"
exit 1
fi
echo_success "Terraform found"
export GITHUB_TOKEN=$(gh auth token)
echo_success "GitHub token exported"
cd "$(dirname "$0")"
echo_info "Initializing Terraform..."
if ! terraform init -input=false; then
echo_error "Terraform initialization failed"
exit 1
fi
echo_success "Terraform initialized"
echo_info "Validating Terraform configuration..."
if ! terraform validate; then
echo_error "Terraform validation failed"
exit 1
fi
echo_success "Terraform configuration valid"
echo_info "Applying Terraform configuration..."
echo_warning "This will create/update GitHub repository settings"
if terraform apply -auto-approve -input=false; then
echo ""
echo_success "Bootstrap complete!"
echo_info "GitHub repository settings have been applied"
else
echo_error "Terraform apply failed"
exit 1
fi
echo_info "Feel free to see your changes here: https://github.com/#{OWNER}/#{app_name}"
BASH
run "chmod +x #{bootstrap_dir}/run"
create_file 'bin/bootstrap_github_repo', <<~BASH, force: true
#!/usr/bin/env bash
set -euo pipefail
echo "Applying GitHub configuration for repository '#{app_name}'..."
tmp/bootstrap_github_repo/run
BASH
run 'chmod +x bin/bootstrap_github_repo'
run 'bin/fastcheck --dangerous', abort_on_failure: true
say_status :info, 'Local Rails Project is setup, feel free to create and prepare github repsositiory using:', :blue
say_status :info, "cd '#{app_name}' && bin/bootstrap_github_repo", :blue
say "\nNext steps for App setup:", :blue
say <<~MSG, :green
------------------------------------------------------------
1. Change into project
cd '#{app_name}'
2. Stage and commit your files:
git add .
git commit -m \"Initial commit\"
3. Create and configure the GitHub repository:
bin/bootstrap_github_repo
4. Link your local repository to GitHub:
git remote add origin git@github.com:#{OWNER}/#{app_name}.git
git push -u origin develop
------------------------------------------------------------
MSG
end
@cb341
Copy link
Author

cb341 commented Oct 13, 2025

@cb341
Copy link
Author

cb341 commented Oct 13, 2025

Will merge once ready!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment