Skip to content

Instantly share code, notes, and snippets.

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

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

Select an option

Save CuddlyBunion341/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
@CuddlyBunion341
Copy link
Author

Will merge once ready!

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