Skip to content

Commit

Permalink
Update Puma configuration to set worker timeout in development enviro…
Browse files Browse the repository at this point in the history
…nment
  • Loading branch information
eoinkelly committed Aug 31, 2024
2 parents 1e3f8fe + 4a1ed87 commit f9db9a3
Show file tree
Hide file tree
Showing 19 changed files with 150 additions and 99 deletions.
5 changes: 2 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version-file: '.node-version'
cache: 'yarn'
Expand Down Expand Up @@ -122,7 +122,6 @@ jobs:
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout this repo
uses: actions/checkout@v4
Expand All @@ -141,7 +140,7 @@ jobs:
sudo ln -s "$PWD/actionlint" /usr/local/bin/actionlint
- name: Install NodeJS
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
cache: 'yarn'
node-version-file: '.node-version'
Expand Down
24 changes: 18 additions & 6 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,24 @@ Rails:
Enabled: true

AllCops:
# Target the oldest version of Ruby that Rails 7 supports
TargetRubyVersion: 2.7
TargetRailsVersion: 7.1
# Target the oldest Ruby that this template's chosen Rails version supports In
# a normal Rails app, Rubocop would infer this from the version of Ruby
# running. We want the rubocop checks that run on this repo to work for any
# version of Ruby that the chosen Rails version supports so we have to be
# explicit.
TargetRubyVersion:
<%=
YAML.safe_load(Pathname.new("./target_versions.yml").realpath.read).fetch("minimum_ruby_major_minor")
%>

# In a normal Rails app, Rubocop would pull this from the Gemfile. We want the
# rubocop checks that run on this repo to be consistent with the checks that
# run on generated apps so we have to be explicit.
TargetRailsVersion:
<%=
YAML.safe_load(Pathname.new("./target_versions.yml").realpath.read).fetch("target_rails_major_minor")
%>

NewCops: enable
DisplayCopNames: true
DisplayStyleGuide: true
Expand Down Expand Up @@ -210,9 +225,6 @@ Rails/Output:
Style/BarePercentLiterals:
EnforcedStyle: percent_q

Style/ClassAndModuleChildren:
Enabled: false

Style/DoubleNegation:
Enabled: false

Expand Down
5 changes: 3 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ GEM
rack (3.0.9.1)
rainbow (3.1.1)
regexp_parser (2.9.0)
rexml (3.2.8)
strscan (>= 3.0.9)
rexml (3.3.6)
strscan
rubocop (1.60.2)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
Expand Down Expand Up @@ -73,6 +73,7 @@ GEM
PLATFORMS
arm64-darwin-21
arm64-darwin-22
arm64-darwin-23
x86_64-linux

DEPENDENCIES
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ This is a template you can use to create new Rails applications.
- [How do I use this?](#how-do-i-use-this)
- [How do I use this template for every Rails app I create?](#how-do-i-use-this-template-for-every-rails-app-i-create)
- [Contributing](#contributing)
- [Updating this template to a new Rails version](#updating-this-template-to-a-new-rails-version)
- [Credits](#credits)

## Background
Expand Down Expand Up @@ -287,6 +288,13 @@ $ bundle install
$ bundle exec rubocop # optionally adding -A for autofixes
```

### Updating this template to a new Rails version

1. Change the Rails and Ruby versions in
[./target_versions.yml](./target_versions.yml) as per the instructions in
that file.
2. Update the template as required to support the new Rails version

## Credits

This repo was forked from
Expand Down
39 changes: 30 additions & 9 deletions ci/bin/build-and-test
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
#!/usr/bin/env ruby

require "fileutils"
require "pathname"
require "yaml"

def ensure_everything_is_committed
out = `git status -s`

raise StandardError, "there are uncommitted changes or un-staged files: #{out}" unless out.empty?
end

def build_rails_version_specifier(target_versions_path)
major_minor = YAML.safe_load(File.read(target_versions_path)).fetch("target_rails_major_minor")

"~> #{major_minor}.0"
end

root_path = File.absolute_path(File.join(__dir__, "../.."))
template_path = File.join(root_path, "template.rb")
builds_path = File.join(root_path, "tmp/builds")
app_name = ENV.fetch("APP_NAME", "app")
app_path = File.join(builds_path, app_name)
target_versions_path = File.join(root_path, "target_versions.yml")
rails_version_specifier = build_rails_version_specifier(target_versions_path)

config_path = if ENV.fetch("CONFIG_PATH", "") == ""
File.join(root_path, "ackama_rails_template.config.yml")
Expand All @@ -32,8 +48,19 @@ puts "=" * 80

raise "Missing config YML file" if config_path && !(File.exist?(config_path) && File.file?(config_path))

puts "Installing latest rails gem"
system "gem install rails --no-document"
puts "Installing latest compatible rails gem"
install_output = `gem install rails --version '#{rails_version_specifier}' --no-document`

puts install_output # for ease of debugging

rails_version = install_output
.split("\n")
.grep(/Successfully installed rails-\d/)
.first
.strip
.sub("Successfully installed rails-", "")

puts "Using rails version #{rails_version}"

unless Dir.exist?(builds_path)
puts "Creating #{builds_path}"
Expand All @@ -47,7 +74,7 @@ end

Dir.chdir(builds_path) do |cwd|
puts "Working dir is now #{cwd}"
cmd = %Q(#{config_env_var} RACK_ENV=development RAILS_ENV=development rails new "#{app_name}" -d postgresql #{skip_flags} -m "#{template_path}")
cmd = %Q(#{config_env_var} TARGET_VERSIONS_PATH="#{target_versions_path}" RACK_ENV=development RAILS_ENV=development rails _#{rails_version}_ new "#{app_name}" -d postgresql #{skip_flags} -m "#{template_path}") # rubocop:disable Layout/LineLength
puts <<~EO_HELP
Build command:
#{cmd}
Expand All @@ -56,12 +83,6 @@ Dir.chdir(builds_path) do |cwd|
system(cmd, exception: true)
end

def ensure_everything_is_committed
out = `git status -s`

raise StandardError, "there are uncommitted changes or un-staged files: #{out}" unless out.empty?
end

puts "Running post-generator checks"
Dir.chdir(app_path) do |cwd|
puts "Working dir is now #{cwd}"
Expand Down
8 changes: 8 additions & 0 deletions target_versions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# This template will work with Rails versions that match the given major.minor
#
# This is stored in a separate file so we can share it between the template and
# our CI configuration.
target_rails_major_minor: '7.1' # specify as major.minor

# Set this to the minimum version of Ruby that the chosen Rails version supports.
minimum_ruby_major_minor: '2.7'
30 changes: 24 additions & 6 deletions template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@
require "shellwords"
require "pp"

RAILS_REQUIREMENT = "~> 7.1.1".freeze

##
# This single template file will be downloaded and run by the `rails new`
# command so all code it needs must be inlined we cannot load other files from
# this repo.
#
class Config
DEFAULT_CONFIG_FILE_PATH = "../ackama_rails_template.config.yml".freeze
DEFAULT_CONFIG_PATH = "../ackama_rails_template.config.yml".freeze
DEFAULT_TARGET_VERSIONS_PATH = "../target_versions.yml".freeze

attr_reader :acceptable_rails_versions_specifier

def initialize
config_file_path = File.absolute_path(ENV.fetch("CONFIG_PATH", DEFAULT_CONFIG_FILE_PATH))
config_file_path = File.absolute_path(ENV.fetch("CONFIG_PATH", DEFAULT_CONFIG_PATH))
@yaml_config = YAML.safe_load(File.read(config_file_path))
@acceptable_rails_versions_specifier = load_acceptable_rails_versions_specifier
end

def staging_hostname
Expand Down Expand Up @@ -60,6 +62,22 @@ def apply_variant_deploy_with_capistrano?
def apply_variant_deploy_with_ackama_ec2_capistrano?
@yaml_config.fetch("apply_variant_deploy_with_ackama_ec2_capistrano")
end

private

##
# Rails version constraint is stored in a separate file so that it can be used
# outside of just the template
#
def load_acceptable_rails_versions_specifier
rel_path = ENV.fetch("TARGET_VERSIONS_PATH", DEFAULT_TARGET_VERSIONS_PATH)
abs_path = Pathname.new(rel_path).realpath
major_minor = YAML.safe_load(abs_path.read).fetch("target_rails_major_minor")

# Use the major.minor we got from the YAML file to build a RubyGems
# compatible specifier that will match all patch versions
"~> #{major_minor}.0"
end
end

class Terminal
Expand Down Expand Up @@ -306,11 +324,11 @@ def add_template_repository_to_source_path
end

def assert_minimum_rails_version
requirement = Gem::Requirement.new(RAILS_REQUIREMENT)
requirement = Gem::Requirement.new(TEMPLATE_CONFIG.acceptable_rails_versions_specifier)
rails_version = Gem::Version.new(Rails::VERSION::STRING)
return if requirement.satisfied_by?(rails_version)

puts "ERROR: This template requires Rails #{RAILS_REQUIREMENT}. You are using #{rails_version}"
puts "ERROR: This template requires Rails #{TEMPLATE_CONFIG.acceptable_rails_versions_specifier}. You are using #{rails_version}"
exit 1
end

Expand Down
2 changes: 1 addition & 1 deletion variants/backend-base/Gemfile.tt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ ruby File.read(".ruby-version")
gem "rails", "<%= Rails.version %>"
gem "puma"
gem "pg"
gem 'dotenv-rails', require: "dotenv/rails-now"
gem 'dotenv-rails', require: "dotenv/load"
gem "bootsnap", require: false

gem "shakapacker"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,25 @@
# Copied from
# https://github.com/rails/rails/blob/main/activestorage/app/controllers/active_storage/base_controller.rb
# :nocov:
class ActiveStorage::BaseController < ActionController::Base # rubocop:disable Rails/ApplicationController
include ActiveStorage::SetCurrent
module ActiveStorage
class BaseController < ActionController::Base # rubocop:disable Rails/ApplicationController
include ActiveStorage::SetCurrent

protect_from_forgery with: :exception
protect_from_forgery with: :exception

before_action :authenticated?
before_action :authenticated?

self.etag_with_template_digest = false
self.etag_with_template_digest = false

private
private

# ActiveStorage defaults to security via obscurity approach to serving links
# If this is acceptable for your use case then this authenticable test can be
# removed. If not then code should be added to only serve files appropriately.
# https://edgeguides.rubyonrails.org/active_storage_overview.html#proxy-mode
def authenticated?
raise StandardError, "No authentication is configured for ActiveStorage"
# ActiveStorage defaults to security via obscurity approach to serving links
# If this is acceptable for your use case then this authenticable test can be
# removed. If not then code should be added to only serve files appropriately.
# https://edgeguides.rubyonrails.org/active_storage_overview.html#proxy-mode
def authenticated?
raise StandardError, "No authentication is configured for ActiveStorage"
end
end
end
# :nocov:
25 changes: 18 additions & 7 deletions variants/backend-base/config/app.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
# Be sure to restart your server when you modify this file.
#
# Use this file to load non-sensitive app config from ENV. Config values here
# will be loaded into `Rails.application.config.app`.
#
# Sensitive config should be put in `config/secrets.yml` (which will load it
# into `Rails.application.secrets`)
# Use this file to load configuration values from the environment, which will
# be accessible by the app through `Rails.application.config.app`

default: &default
# Your secret key is used for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!
# Make sure the secret is at least 30 characters and all random,
# no regular words, or you'll be exposed to dictionary attacks.
# You can use `rails secret` to generate a secure secret key.
secret_key_base: "<%= ENV.fetch('RAILS_SECRET_KEY_BASE') %>"

default:
&default # The default `From:` address to use for email sent by this application
# The default `From:` address to use for email sent by this application
mail_from: "<%= ENV['MAIL_FROM'] %>"

# this should either begin with GTM- (for a container) or G- (for a tag)
google_analytics_id: "<%= ENV.fetch('GOOGLE_ANALYTICS_ID', nil) %>"

active_record_encryption_primary_key:
"<%= ENV.fetch('ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY') %>"
active_record_encryption_deterministic_key:
"<%= ENV.fetch('ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY') %>"
active_record_encryption_key_derivation_salt:
"<%= ENV.fetch('ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT') %>"

development:
<<: *default

Expand Down
16 changes: 8 additions & 8 deletions variants/backend-base/config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@
# load config/app.yml into Rails.application.config.app.*
config.app = config_for(:app)
# pull the secret_key_base from our app config
config.secret_key_base = config.app.secret_key_base
config.middleware.insert_before Rack::Sendfile, HttpBasicAuth
config.action_dispatch.default_headers["Permissions-Policy"] = "interest-cohort=()"
# ActiveRecord encrypted attributes expectes to find the key secrets under
# `config.active_record.encryption.*`. If the secrets were stored in Rails
# encrypted credentials file then Rails would map them automatically for us.
# We prefer to store the secrets in the ENV and load them through
# `config/secrets.yml` so we have to manually assign them here.
config.active_record.encryption.primary_key = Rails.application.secrets.active_record_encryption_primary_key
config.active_record.encryption.deterministic_key = Rails.application.secrets.active_record_encryption_deterministic_key
config.active_record.encryption.key_derivation_salt = Rails.application.secrets.active_record_encryption_key_derivation_salt
# Configure the encryption key for ActiveRecord encrypted attributes with values from our app config,
# as Rails only automatically picks them up if they're sourced from `config/credentials.yml.enc`
config.active_record.encryption.primary_key = Rails.application.config.app.active_record_encryption_primary_key
config.active_record.encryption.deterministic_key = Rails.application.config.app.active_record_encryption_deterministic_key
config.active_record.encryption.key_derivation_salt = Rails.application.config.app.active_record_encryption_key_derivation_salt
config.action_dispatch.default_headers["X-Frame-Options"] = "DENY"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@
# `*_src` method.
#
# asset_host = if Rails.env.development? || Rails.env.test?
# "http://#{Rails.application.secrets.asset_host}"
# "http://#{Rails.application.config.app.asset_host}"
# else
# "https://#{Rails.application.secrets.asset_host}"
# "https://#{Rails.application.config.app.asset_host}"
# end
# policy.default_src :self, asset_host

Expand Down
Loading

0 comments on commit f9db9a3

Please sign in to comment.