Upgrade to Rails 8 and Ruby 3.4.5 (#1)

* Bump Ruby to 3.4.5
* Update dependencies
* Adjust for Rails 8 and Ruby 3.5 API changes
* Mark params strings as mutable in prepapration for frozen strings in Ruby 3.5
* Update test for HTML5 sanitizer
    With Rails 7.1 the HTML5 sanitizer became the default, this breakts this test because the old sanitizer used to delete unpermitted nodes, while the new one returns their content
    The final string is safe, but different then it used to be in Rails 7.0
* Remove direct Turbo tesh helpers require & parallelize tests
* Fix Zeitwerk issues with rails extensions
* Update Resque setup for Redis 5+
* Remove unused views
* Remove GID v1 handler
This commit is contained in:
Stanko Krtalić
2025-09-02 17:02:41 +02:00
committed by GitHub
parent 657d4edcd4
commit eecdb29332
39 changed files with 508 additions and 417 deletions

View File

@@ -1 +1 @@
3.3.1 3.4.5

View File

@@ -1,17 +1,24 @@
# syntax = docker/dockerfile:1 # syntax = docker/dockerfile:1
# Make sure it matches the Ruby version in .ruby-version and Gemfile # Make sure it matches the Ruby version in .ruby-version and Gemfile
ARG RUBY_VERSION=3.3.1 ARG RUBY_VERSION=3.4.5
FROM ruby:$RUBY_VERSION-slim AS base FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base
# Rails app lives here # Rails app lives here
WORKDIR /rails WORKDIR /rails
# Install base packages
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y curl libsqlite3-0 libvips libjemalloc2 ffmpeg redis && \
ln -s /usr/lib/$(uname -m)-linux-gnu/libjemalloc.so.2 /usr/local/lib/libjemalloc.so && \
rm -rf /var/lib/apt/lists /var/cache/apt/archive
# Set production environment # Set production environment
ENV RAILS_ENV="production" \ ENV RAILS_ENV="production" \
BUNDLE_DEPLOYMENT="1" \ BUNDLE_DEPLOYMENT="1" \
BUNDLE_PATH="/usr/local/bundle" \ BUNDLE_PATH="/usr/local/bundle" \
BUNDLE_WITHOUT="development" BUNDLE_WITHOUT="development" \
LD_PRELOAD="/usr/local/lib/libjemalloc.so"
# Throw-away build stage to reduce size of final image # Throw-away build stage to reduce size of final image
@@ -19,10 +26,12 @@ FROM base AS build
# Install packages need to build gems # Install packages need to build gems
RUN apt-get update -qq && \ RUN apt-get update -qq && \
apt-get install -y build-essential git pkg-config apt-get install -y build-essential git pkg-config libyaml-dev && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
# Install application gems # Install application gems
COPY Gemfile Gemfile.lock ./ COPY Gemfile Gemfile.lock vendor ./
RUN bundle install && \ RUN bundle install && \
rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git
@@ -36,20 +45,16 @@ RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile
# Final stage for app image # Final stage for app image
FROM base FROM base
# Run and own only the runtime files as a non-root user for security
RUN groupadd --system --gid 1000 rails && \
useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash
USER 1000:1000
# Configure environment defaults # Configure environment defaults
ENV HTTP_IDLE_TIMEOUT=60 ENV HTTP_IDLE_TIMEOUT=60
ENV HTTP_READ_TIMEOUT=300 ENV HTTP_READ_TIMEOUT=300
ENV HTTP_WRITE_TIMEOUT=300 ENV HTTP_WRITE_TIMEOUT=300
# Install packages needed to run the application
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y libsqlite3-0 libvips curl ffmpeg redis && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
# Run and own the application files as a non-root user for security
RUN useradd rails
USER rails:rails
# Copy built artifacts: gems, application # Copy built artifacts: gems, application
COPY --from=build --chown=rails:rails /usr/local/bundle /usr/local/bundle COPY --from=build --chown=rails:rails /usr/local/bundle /usr/local/bundle
COPY --from=build --chown=rails:rails /rails /rails COPY --from=build --chown=rails:rails /rails /rails

View File

@@ -7,14 +7,14 @@ git_source(:bc) { |repo| "https://github.com/basecamp/#{repo}" }
gem "rails", github: "rails/rails", branch: "main" gem "rails", github: "rails/rails", branch: "main"
# Drivers # Drivers
gem "sqlite3", "~> 1.4" gem "sqlite3", "~> 2.7"
gem "redis", "~> 4.0" gem "redis", "~> 5.4"
# Deployment # Deployment
gem "puma", "~> 6.4" gem "puma", "~> 6.6"
# Jobs # Jobs
gem "resque", "~> 2.6.0" gem "resque", "~> 2.7.0"
gem "resque-pool", "~> 0.7.1" gem "resque-pool", "~> 0.7.1"
# Assets # Assets

View File

@@ -1,190 +1,204 @@
GIT GIT
remote: https://github.com/hotwired/turbo-rails.git remote: https://github.com/hotwired/turbo-rails.git
revision: d7155dd51d8aeaa7128ab50a62b5cd3cb91b84f1 revision: 30cd8fcc6f82c1ad4edd1ed6069ba878f21f02b3
specs: specs:
turbo-rails (2.0.0) turbo-rails (2.0.16)
actionpack (>= 6.0.0) actionpack (>= 7.1.0)
activejob (>= 6.0.0) railties (>= 7.1.0)
railties (>= 6.0.0)
GIT GIT
remote: https://github.com/rails/importmap-rails.git remote: https://github.com/rails/importmap-rails.git
revision: be74dead314957833f5d09e05a8daaa3526a964b revision: 51c1a531327fc04ed4552bb0fd523eb43561b817
specs: specs:
importmap-rails (2.0.1) importmap-rails (2.2.2)
actionpack (>= 6.0.0) actionpack (>= 6.0.0)
activesupport (>= 6.0.0) activesupport (>= 6.0.0)
railties (>= 6.0.0) railties (>= 6.0.0)
GIT GIT
remote: https://github.com/rails/propshaft.git remote: https://github.com/rails/propshaft.git
revision: e7cdbcd4aa9c9a390dade263bbd7c7cedc0c6442 revision: e49a9de659ff27462015e54dd832e86e762a6ddc
specs: specs:
propshaft (0.8.0) propshaft (1.2.1)
actionpack (>= 7.0.0) actionpack (>= 7.0.0)
activesupport (>= 7.0.0) activesupport (>= 7.0.0)
rack rack
railties (>= 7.0.0)
GIT GIT
remote: https://github.com/rails/rails.git remote: https://github.com/rails/rails.git
revision: 776626ff987a96201b0bdbd86d716ca6698fa8b3 revision: 02660cd9e84aa57c6cf3bf6e08cc18e18292e2fa
branch: main branch: main
specs: specs:
actioncable (7.2.0.alpha) actioncable (8.1.0.alpha)
actionpack (= 7.2.0.alpha) actionpack (= 8.1.0.alpha)
activesupport (= 7.2.0.alpha) activesupport (= 8.1.0.alpha)
nio4r (~> 2.0) nio4r (~> 2.0)
websocket-driver (>= 0.6.1) websocket-driver (>= 0.6.1)
zeitwerk (~> 2.6) zeitwerk (~> 2.6)
actionmailbox (7.2.0.alpha) actionmailbox (8.1.0.alpha)
actionpack (= 7.2.0.alpha) actionpack (= 8.1.0.alpha)
activejob (= 7.2.0.alpha) activejob (= 8.1.0.alpha)
activerecord (= 7.2.0.alpha) activerecord (= 8.1.0.alpha)
activestorage (= 7.2.0.alpha) activestorage (= 8.1.0.alpha)
activesupport (= 7.2.0.alpha) activesupport (= 8.1.0.alpha)
mail (>= 2.8.0) mail (>= 2.8.0)
actionmailer (7.2.0.alpha) actionmailer (8.1.0.alpha)
actionpack (= 7.2.0.alpha) actionpack (= 8.1.0.alpha)
actionview (= 7.2.0.alpha) actionview (= 8.1.0.alpha)
activejob (= 7.2.0.alpha) activejob (= 8.1.0.alpha)
activesupport (= 7.2.0.alpha) activesupport (= 8.1.0.alpha)
mail (>= 2.8.0) mail (>= 2.8.0)
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
actionpack (7.2.0.alpha) actionpack (8.1.0.alpha)
actionview (= 7.2.0.alpha) actionview (= 8.1.0.alpha)
activesupport (= 7.2.0.alpha) activesupport (= 8.1.0.alpha)
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
racc
rack (>= 2.2.4) rack (>= 2.2.4)
rack-session (>= 1.0.1) rack-session (>= 1.0.1)
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6) rails-html-sanitizer (~> 1.6)
useragent (~> 0.16) useragent (~> 0.16)
actiontext (7.2.0.alpha) actiontext (8.1.0.alpha)
actionpack (= 7.2.0.alpha) action_text-trix (~> 2.1.15)
activerecord (= 7.2.0.alpha) actionpack (= 8.1.0.alpha)
activestorage (= 7.2.0.alpha) activerecord (= 8.1.0.alpha)
activesupport (= 7.2.0.alpha) activestorage (= 8.1.0.alpha)
activesupport (= 8.1.0.alpha)
globalid (>= 0.6.0) globalid (>= 0.6.0)
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
actionview (7.2.0.alpha) actionview (8.1.0.alpha)
activesupport (= 7.2.0.alpha) activesupport (= 8.1.0.alpha)
builder (~> 3.1) builder (~> 3.1)
erubi (~> 1.11) erubi (~> 1.11)
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6) rails-html-sanitizer (~> 1.6)
activejob (7.2.0.alpha) activejob (8.1.0.alpha)
activesupport (= 7.2.0.alpha) activesupport (= 8.1.0.alpha)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (7.2.0.alpha) activemodel (8.1.0.alpha)
activesupport (= 7.2.0.alpha) activesupport (= 8.1.0.alpha)
activerecord (7.2.0.alpha) activerecord (8.1.0.alpha)
activemodel (= 7.2.0.alpha) activemodel (= 8.1.0.alpha)
activesupport (= 7.2.0.alpha) activesupport (= 8.1.0.alpha)
timeout (>= 0.4.0) timeout (>= 0.4.0)
activestorage (7.2.0.alpha) activestorage (8.1.0.alpha)
actionpack (= 7.2.0.alpha) actionpack (= 8.1.0.alpha)
activejob (= 7.2.0.alpha) activejob (= 8.1.0.alpha)
activerecord (= 7.2.0.alpha) activerecord (= 8.1.0.alpha)
activesupport (= 7.2.0.alpha) activesupport (= 8.1.0.alpha)
marcel (~> 1.0) marcel (~> 1.0)
activesupport (7.2.0.alpha) activesupport (8.1.0.alpha)
base64 base64
benchmark (>= 0.3)
bigdecimal bigdecimal
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.3.1)
connection_pool (>= 2.2.5) connection_pool (>= 2.2.5)
drb drb
i18n (>= 1.6, < 2) i18n (>= 1.6, < 2)
logger (>= 1.4.2)
minitest (>= 5.1) minitest (>= 5.1)
securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5) tzinfo (~> 2.0, >= 2.0.5)
rails (7.2.0.alpha) uri (>= 0.13.1)
actioncable (= 7.2.0.alpha) rails (8.1.0.alpha)
actionmailbox (= 7.2.0.alpha) actioncable (= 8.1.0.alpha)
actionmailer (= 7.2.0.alpha) actionmailbox (= 8.1.0.alpha)
actionpack (= 7.2.0.alpha) actionmailer (= 8.1.0.alpha)
actiontext (= 7.2.0.alpha) actionpack (= 8.1.0.alpha)
actionview (= 7.2.0.alpha) actiontext (= 8.1.0.alpha)
activejob (= 7.2.0.alpha) actionview (= 8.1.0.alpha)
activemodel (= 7.2.0.alpha) activejob (= 8.1.0.alpha)
activerecord (= 7.2.0.alpha) activemodel (= 8.1.0.alpha)
activestorage (= 7.2.0.alpha) activerecord (= 8.1.0.alpha)
activesupport (= 7.2.0.alpha) activestorage (= 8.1.0.alpha)
activesupport (= 8.1.0.alpha)
bundler (>= 1.15.0) bundler (>= 1.15.0)
railties (= 7.2.0.alpha) railties (= 8.1.0.alpha)
railties (7.2.0.alpha) railties (8.1.0.alpha)
actionpack (= 7.2.0.alpha) actionpack (= 8.1.0.alpha)
activesupport (= 7.2.0.alpha) activesupport (= 8.1.0.alpha)
irb irb (~> 1.13)
rackup (>= 1.0.0) rackup (>= 1.0.0)
rake (>= 12.2) rake (>= 12.2)
thor (~> 1.0, >= 1.2.2) thor (~> 1.0, >= 1.2.2)
tsort (>= 0.2)
zeitwerk (~> 2.6) zeitwerk (~> 2.6)
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
addressable (2.8.6) action_text-trix (2.1.15)
public_suffix (>= 2.0.2, < 6.0) railties
ast (2.4.2) addressable (2.8.7)
base64 (0.2.0) public_suffix (>= 2.0.2, < 7.0)
ast (2.4.3)
base64 (0.3.0)
bcrypt (3.1.20) bcrypt (3.1.20)
bigdecimal (3.1.6) benchmark (0.4.1)
brakeman (6.1.1) bigdecimal (3.2.2)
brakeman (7.1.0)
racc racc
builder (3.2.4) builder (3.3.0)
capybara (3.39.2) capybara (3.40.0)
addressable addressable
matrix matrix
mini_mime (>= 0.1.3) mini_mime (>= 0.1.3)
nokogiri (~> 1.8) nokogiri (~> 1.11)
rack (>= 1.6.0) rack (>= 1.6.0)
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
regexp_parser (>= 1.5, < 3.0) regexp_parser (>= 1.5, < 3.0)
xpath (~> 3.2) xpath (~> 3.2)
chunky_png (1.4.0) chunky_png (1.4.0)
concurrent-ruby (1.2.3) concurrent-ruby (1.3.5)
connection_pool (2.4.1) connection_pool (2.5.3)
crack (0.4.5) crack (1.0.0)
bigdecimal
rexml rexml
crass (1.0.6) crass (1.0.6)
date (3.3.4) date (3.4.1)
debug (1.9.1) debug (1.11.0)
irb (~> 1.10) irb (~> 1.10)
reline (>= 0.3.8) reline (>= 0.3.8)
drb (2.2.0) drb (2.2.3)
ruby2_keywords erb (5.0.2)
erubi (1.12.0) erubi (1.13.1)
faker (3.2.2) faker (3.5.2)
i18n (>= 1.8.11, < 2) i18n (>= 1.8.11, < 2)
ffi (1.16.3) ffi (1.17.2-aarch64-linux-gnu)
ffi (1.17.2-arm64-darwin)
ffi (1.17.2-x86_64-darwin)
ffi (1.17.2-x86_64-linux-gnu)
geared_pagination (1.2.0) geared_pagination (1.2.0)
activesupport (>= 5.0) activesupport (>= 5.0)
addressable (>= 2.5.0) addressable (>= 2.5.0)
globalid (1.2.1) globalid (1.2.1)
activesupport (>= 6.1) activesupport (>= 6.1)
hashdiff (1.1.0) hashdiff (1.2.0)
i18n (1.14.1) i18n (1.14.7)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
image_processing (1.12.2) image_processing (1.14.0)
mini_magick (>= 4.9.5, < 5) mini_magick (>= 4.9.5, < 6)
ruby-vips (>= 2.0.17, < 3) ruby-vips (>= 2.0.17, < 3)
io-console (0.7.2) io-console (0.8.1)
irb (1.11.1) irb (1.15.2)
rdoc pp (>= 0.6.0)
rdoc (>= 4.0.0)
reline (>= 0.4.2) reline (>= 0.4.2)
jbuilder (2.11.5) jbuilder (2.14.1)
actionview (>= 5.0.0) actionview (>= 7.0.0)
activesupport (>= 5.0.0) activesupport (>= 7.0.0)
json (2.7.1) json (2.13.2)
jwt (2.7.1) jwt (3.1.2)
kredis (1.7.0) base64
kredis (1.8.0)
activemodel (>= 6.0.0) activemodel (>= 6.0.0)
activesupport (>= 6.0.0) activesupport (>= 6.0.0)
redis (>= 4.2, < 6) redis (>= 4.2, < 6)
language_server-protocol (3.17.0.3) language_server-protocol (3.17.0.5)
loofah (2.22.0) lint_roller (1.1.0)
logger (1.7.0)
loofah (2.24.1)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.12.0) nokogiri (>= 1.12.0)
mail (2.8.1) mail (2.8.1)
@@ -192,177 +206,198 @@ GEM
net-imap net-imap
net-pop net-pop
net-smtp net-smtp
marcel (1.0.2) marcel (1.0.4)
matrix (0.4.2) matrix (0.4.3)
mini_magick (4.12.0) mini_magick (5.3.1)
logger
mini_mime (1.1.5) mini_mime (1.1.5)
minitest (5.22.1) minitest (5.25.5)
mocha (2.1.0) mocha (2.7.1)
ruby2_keywords (>= 0.0.5) ruby2_keywords (>= 0.0.5)
mono_logger (1.1.2) mono_logger (1.1.2)
multi_json (1.15.0) multi_json (1.17.0)
mustermann (3.0.0) mustermann (3.0.4)
ruby2_keywords (~> 0.0.1) ruby2_keywords (~> 0.0.1)
net-http-persistent (4.0.2) net-http-persistent (4.0.6)
connection_pool (~> 2.2) connection_pool (~> 2.2, >= 2.2.4)
net-imap (0.4.9.1) net-imap (0.5.9)
date date
net-protocol net-protocol
net-pop (0.1.2) net-pop (0.1.2)
net-protocol net-protocol
net-protocol (0.2.2) net-protocol (0.2.2)
timeout timeout
net-smtp (0.4.0.1) net-smtp (0.5.1)
net-protocol net-protocol
nio4r (2.7.0) nio4r (2.7.4)
nokogiri (1.16.2-aarch64-linux) nokogiri (1.18.9-aarch64-linux-gnu)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.16.2-arm64-darwin) nokogiri (1.18.9-arm64-darwin)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.16.2-x86_64-darwin) nokogiri (1.18.9-x86_64-darwin)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.16.2-x86_64-linux) nokogiri (1.18.9-x86_64-linux-gnu)
racc (~> 1.4) racc (~> 1.4)
openssl (3.2.0) openssl (3.3.0)
parallel (1.24.0) parallel (1.27.0)
parser (3.3.0.3) parser (3.3.9.0)
ast (~> 2.4.1) ast (~> 2.4.1)
racc racc
platform_agent (1.0.1) platform_agent (1.0.1)
activesupport (>= 5.2.0) activesupport (>= 5.2.0)
useragent (~> 0.16.3) useragent (~> 0.16.3)
psych (5.1.2) pp (0.6.2)
prettyprint
prettyprint (0.2.0)
prism (1.4.0)
psych (5.2.6)
date
stringio stringio
public_suffix (5.0.4) public_suffix (6.0.2)
puma (6.4.2) puma (6.6.1)
nio4r (~> 2.0) nio4r (~> 2.0)
racc (1.7.3) racc (1.8.1)
rack (2.2.8) rack (3.2.0)
rack-protection (3.2.0) rack-protection (4.1.1)
base64 (>= 0.1.0) base64 (>= 0.1.0)
rack (~> 2.2, >= 2.2.4) logger (>= 1.6.0)
rack-session (1.0.2) rack (>= 3.0.0, < 4)
rack (< 3) rack-session (2.1.1)
rack-test (2.1.0) base64 (>= 0.1.0)
rack (>= 3.0.0)
rack-test (2.2.0)
rack (>= 1.3) rack (>= 1.3)
rackup (1.0.0) rackup (2.2.1)
rack (< 3) rack (>= 3)
webrick rails-dom-testing (2.3.0)
rails-dom-testing (2.2.0)
activesupport (>= 5.0.0) activesupport (>= 5.0.0)
minitest minitest
nokogiri (>= 1.6) nokogiri (>= 1.6)
rails-html-sanitizer (1.6.0) rails-html-sanitizer (1.6.2)
loofah (~> 2.21) loofah (~> 2.21)
nokogiri (~> 1.14) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
rails_autolink (1.1.8) rails_autolink (1.1.8)
actionview (> 3.1) actionview (> 3.1)
activesupport (> 3.1) activesupport (> 3.1)
railties (> 3.1) railties (> 3.1)
rainbow (3.1.1) rainbow (3.1.1)
rake (13.1.0) rake (13.3.0)
rdoc (6.6.2) rdoc (6.14.2)
erb
psych (>= 4.0.0) psych (>= 4.0.0)
redis (4.8.1) redis (5.4.1)
redis-client (>= 0.22.0)
redis-client (0.25.2)
connection_pool
redis-namespace (1.11.0) redis-namespace (1.11.0)
redis (>= 4) redis (>= 4)
regexp_parser (2.9.0) regexp_parser (2.11.2)
reline (0.4.2) reline (0.6.2)
io-console (~> 0.5) io-console (~> 0.5)
resque (2.6.0) resque (2.7.0)
mono_logger (~> 1.0) mono_logger (~> 1)
multi_json (~> 1.0) multi_json (~> 1.0)
redis-namespace (~> 1.6) redis-namespace (~> 1.6)
sinatra (>= 0.9.2) sinatra (>= 0.9.2)
resque-pool (0.7.1) resque-pool (0.7.1)
rake (>= 10.0, < 14.0) rake (>= 10.0, < 14.0)
resque (>= 1.22, < 3) resque (>= 1.22, < 3)
rexml (3.2.6) rexml (3.4.1)
rqrcode (2.2.0) rqrcode (3.1.0)
chunky_png (~> 1.0) chunky_png (~> 1.0)
rqrcode_core (~> 1.0) rqrcode_core (~> 2.0)
rqrcode_core (1.2.0) rqrcode_core (2.0.0)
rubocop (1.59.0) rubocop (1.80.0)
json (~> 2.3) json (~> 2.3)
language_server-protocol (>= 3.17.0) language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
parallel (~> 1.10) parallel (~> 1.10)
parser (>= 3.2.2.4) parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0) rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0) regexp_parser (>= 2.9.3, < 3.0)
rexml (>= 3.2.5, < 4.0) rubocop-ast (>= 1.46.0, < 2.0)
rubocop-ast (>= 1.30.0, < 2.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0) unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.30.0) rubocop-ast (1.46.0)
parser (>= 3.2.1.0) parser (>= 3.3.7.2)
rubocop-minitest (0.34.4) prism (~> 1.4)
rubocop (>= 1.39, < 2.0) rubocop-performance (1.25.0)
rubocop-ast (>= 1.30.0, < 2.0) lint_roller (~> 1.1)
rubocop-performance (1.20.2) rubocop (>= 1.75.0, < 2.0)
rubocop (>= 1.48.1, < 2.0) rubocop-ast (>= 1.38.0, < 2.0)
rubocop-ast (>= 1.30.0, < 2.0) rubocop-rails (2.33.3)
rubocop-rails (2.23.1)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
lint_roller (~> 1.1)
rack (>= 1.1) rack (>= 1.1)
rubocop (>= 1.33.0, < 2.0) rubocop (>= 1.75.0, < 2.0)
rubocop-ast (>= 1.30.0, < 2.0) rubocop-ast (>= 1.44.0, < 2.0)
rubocop-rails-omakase (1.0.0) rubocop-rails-omakase (1.1.0)
rubocop rubocop (>= 1.72)
rubocop-minitest rubocop-performance (>= 1.24)
rubocop-performance rubocop-rails (>= 2.30)
rubocop-rails
ruby-progressbar (1.13.0) ruby-progressbar (1.13.0)
ruby-vips (2.2.0) ruby-vips (2.2.5)
ffi (~> 1.12) ffi (~> 1.12)
logger
ruby2_keywords (0.0.5) ruby2_keywords (0.0.5)
rubyzip (2.3.2) rubyzip (3.0.2)
selenium-webdriver (4.16.0) securerandom (0.4.1)
selenium-webdriver (4.35.0)
base64 (~> 0.2)
logger (~> 1.4)
rexml (~> 3.2, >= 3.2.5) rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0) rubyzip (>= 1.2.2, < 4.0)
websocket (~> 1.0) websocket (~> 1.0)
sentry-rails (5.16.1) sentry-rails (5.26.0)
railties (>= 5.0) railties (>= 5.0)
sentry-ruby (~> 5.16.1) sentry-ruby (~> 5.26.0)
sentry-ruby (5.16.1) sentry-ruby (5.26.0)
bigdecimal
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
sinatra (3.2.0) sinatra (4.1.1)
logger (>= 1.6.0)
mustermann (~> 3.0) mustermann (~> 3.0)
rack (~> 2.2, >= 2.2.4) rack (>= 3.0.0, < 4)
rack-protection (= 3.2.0) rack-protection (= 4.1.1)
rack-session (>= 2.0.0, < 3)
tilt (~> 2.0) tilt (~> 2.0)
sqlite3 (1.7.0-aarch64-linux) sqlite3 (2.7.3-aarch64-linux-gnu)
sqlite3 (1.7.0-arm64-darwin) sqlite3 (2.7.3-arm64-darwin)
sqlite3 (1.7.0-x86_64-darwin) sqlite3 (2.7.3-x86_64-darwin)
sqlite3 (1.7.0-x86_64-linux) sqlite3 (2.7.3-x86_64-linux-gnu)
stimulus-rails (1.3.3) stimulus-rails (1.3.4)
railties (>= 6.0.0) railties (>= 6.0.0)
stringio (3.1.0) stringio (3.1.7)
thor (1.3.0) thor (1.4.0)
thruster (0.1.7-aarch64-linux) thruster (0.1.15-aarch64-linux)
thruster (0.1.7-arm64-darwin) thruster (0.1.15-arm64-darwin)
thruster (0.1.7-x86_64-darwin) thruster (0.1.15-x86_64-darwin)
thruster (0.1.7-x86_64-linux) thruster (0.1.15-x86_64-linux)
tilt (2.3.0) tilt (2.6.1)
timeout (0.4.1) timeout (0.4.3)
tsort (0.2.0)
tzinfo (2.0.6) tzinfo (2.0.6)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
unicode-display_width (2.5.0) unicode-display_width (3.1.5)
useragent (0.16.10) unicode-emoji (~> 4.0, >= 4.0.4)
web-push (3.0.1) unicode-emoji (4.0.4)
jwt (~> 2.0) uri (1.0.3)
useragent (0.16.11)
web-push (3.0.2)
jwt (~> 3.0)
openssl (~> 3.0) openssl (~> 3.0)
webmock (3.19.1) webmock (3.25.1)
addressable (>= 2.8.0) addressable (>= 2.8.0)
crack (>= 0.3.2) crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0) hashdiff (>= 0.4.0, < 2.0.0)
webrick (1.8.1) websocket (1.2.11)
websocket (1.2.10) websocket-driver (0.8.0)
websocket-driver (0.7.6) base64
websocket-extensions (>= 0.1.0) websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5) websocket-extensions (0.1.5)
xpath (3.2.0) xpath (3.2.0)
nokogiri (~> 1.8) nokogiri (~> 1.8)
zeitwerk (2.6.13) zeitwerk (2.7.3)
PLATFORMS PLATFORMS
aarch64-linux aarch64-linux
@@ -385,18 +420,18 @@ DEPENDENCIES
net-http-persistent net-http-persistent
platform_agent platform_agent
propshaft! propshaft!
puma (~> 6.4) puma (~> 6.6)
rails! rails!
rails_autolink rails_autolink
redis (~> 4.0) redis (~> 5.4)
resque (~> 2.6.0) resque (~> 2.7.0)
resque-pool (~> 0.7.1) resque-pool (~> 0.7.1)
rqrcode rqrcode
rubocop-rails-omakase rubocop-rails-omakase
selenium-webdriver selenium-webdriver
sentry-rails sentry-rails
sentry-ruby sentry-ruby
sqlite3 (~> 1.4) sqlite3 (~> 2.7)
stimulus-rails stimulus-rails
thruster thruster
turbo-rails! turbo-rails!

View File

@@ -1,4 +1,7 @@
class Rooms::ClosedsController < RoomsController class Rooms::ClosedsController < RoomsController
before_action :set_room, only: %i[ show edit update ]
before_action :ensure_can_administer, only: %i[ update ]
before_action :remember_last_room_visited, only: :show
before_action :force_room_type, only: %i[ edit update ] before_action :force_room_type, only: %i[ edit update ]
DEFAULT_ROOM_NAME = "New room" DEFAULT_ROOM_NAME = "New room"

View File

@@ -1,6 +1,8 @@
class Rooms::InvolvementsController < ApplicationController class Rooms::InvolvementsController < ApplicationController
include RoomScoped include RoomScoped
before_action :ensure_can_administer, only: %i[ update ]
def show def show
@involvement = @membership.involvement @involvement = @membership.involvement
end end

View File

@@ -1,4 +1,7 @@
class Rooms::OpensController < RoomsController class Rooms::OpensController < RoomsController
before_action :set_room, only: %i[ show edit update ]
before_action :ensure_can_administer, only: %i[ update ]
before_action :remember_last_room_visited, only: :show
before_action :force_room_type, only: %i[ edit update ] before_action :force_room_type, only: %i[ edit update ]
DEFAULT_ROOM_NAME = "New room" DEFAULT_ROOM_NAME = "New room"

View File

@@ -1,6 +1,6 @@
class RoomsController < ApplicationController class RoomsController < ApplicationController
before_action :set_room, only: %i[ edit update show destroy ] before_action :set_room, only: %i[ show destroy ]
before_action :ensure_can_administer, only: %i[ update destroy ] before_action :ensure_can_administer, only: %i[ destroy ]
before_action :remember_last_room_visited, only: :show before_action :remember_last_room_visited, only: :show
def index def index

View File

@@ -6,7 +6,7 @@ class Membership < ApplicationRecord
after_destroy_commit { user.reset_remote_connections } after_destroy_commit { user.reset_remote_connections }
enum involvement: %w[ invisible nothing mentions everything ].index_by(&:itself), _prefix: :involved_in enum :involvement, %w[ invisible nothing mentions everything ].index_by(&:itself), prefix: :involved_in
scope :with_ordered_room, -> { includes(:room).joins(:room).order("LOWER(rooms.name)") } scope :with_ordered_room, -> { includes(:room).joins(:room).order("LOWER(rooms.name)") }
scope :without_direct_rooms, -> { joins(:room).where.not(room: { type: "Rooms::Direct" }) } scope :without_direct_rooms, -> { joins(:room).where.not(room: { type: "Rooms::Direct" }) }

View File

@@ -53,12 +53,12 @@ class Opengraph::Fetch
# the body of any large responses. But that header could be wrong or # the body of any large responses. But that header could be wrong or
# missing. To be on the safe side, we'll read the body in chunks, and bail # missing. To be on the safe side, we'll read the body in chunks, and bail
# if it runs over our size limit. # if it runs over our size limit.
"".tap do |body| StringIO.new.tap do |body|
response.read_body do |chunk| response.read_body do |chunk|
return nil if body.bytesize + chunk.bytesize > MAX_BODY_SIZE return nil if body.string.bytesize + chunk.bytesize > MAX_BODY_SIZE
body << chunk body << chunk
end end
end end.string
end end
def response_valid?(response) def response_valid?(response)

View File

@@ -2,7 +2,7 @@ module User::Role
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do
enum role: %i[ member administrator bot ] enum :role, %i[ member administrator bot ]
end end
def can_administer?(record = nil) def can_administer?(record = nil)

View File

@@ -53,7 +53,7 @@ class Webhook < ApplicationRecord
end end
def extract_text_from(response) def extract_text_from(response)
response.body.force_encoding("UTF-8") if response.code == "200" && response.content_type.in?(%w[ text/html text/plain ]) response.body.dup.force_encoding("UTF-8") if response.code == "200" && response.content_type.in?(%w[ text/html text/plain ])
end end
def receive_text_reply_to(room, text:) def receive_text_reply_to(room, text:)

View File

@@ -1,27 +1,7 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
# The application 'brakeman' is installed as part of a gem, and
# this file is here to facilitate running it.
#
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
bundle_binstub = File.expand_path("bundle", __dir__)
if File.file?(bundle_binstub)
if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
end
end
require "rubygems" require "rubygems"
require "bundler/setup" require "bundler/setup"
ARGV.unshift("--ensure-latest")
load Gem.bin_path("brakeman", "brakeman") load Gem.bin_path("brakeman", "brakeman")

6
bin/bundler-audit Executable file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env ruby
require_relative "../config/boot"
require "bundler/audit/cli"
ARGV.concat %w[ --config config/bundler-audit.yml ] if ARGV.empty? || ARGV.include?("check")
Bundler::Audit::CLI.start

6
bin/ci Executable file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env ruby
require_relative "../config/boot"
require "active_support/continuous_integration"
CI = ActiveSupport::ContinuousIntegration
require_relative "../config/ci.rb"

2
bin/dev Executable file
View File

@@ -0,0 +1,2 @@
#!/usr/bin/env ruby
exec "./bin/rails", "server", *ARGV

View File

@@ -1,27 +1,8 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
# The application 'rubocop' is installed as part of a gem, and
# this file is here to facilitate running it.
#
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
bundle_binstub = File.expand_path("bundle", __dir__)
if File.file?(bundle_binstub)
if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
end
end
require "rubygems" require "rubygems"
require "bundler/setup" require "bundler/setup"
# Explicit RuboCop config increases performance slightly while avoiding config confusion.
ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__))
load Gem.bin_path("rubocop", "rubocop") load Gem.bin_path("rubocop", "rubocop")

View File

@@ -1,45 +1,32 @@
#!/bin/bash #!/usr/bin/env ruby
set -eo pipefail require "fileutils"
app_root="$( cd "$(dirname "$0")/.."; pwd )" APP_ROOT = File.expand_path("..", __dir__)
app_name=campfire
# Use application binstubs def system!(*args)
export PATH="$app_root/bin:$PATH" system(*args, exception: true)
end
announce() { if ENV["RAILS_ENV"] == "production"
echo puts "RAILS_ENV is production; bailing out"
echo "--- $@"
}
if [ "$RAILS_ENV" == "production" ]
then
echo "RAILS_ENV is production; bailing out"
exit exit
fi end
announce "Installing dependencies" FileUtils.chdir APP_ROOT do
mise install puts "== Installing dependencies =="
bundle install system "mise install"
system("bundle check") || system!("bundle install")
announce "Preparing database" puts "\n== Preparing database =="
if [[ $* == *--reset* ]]; then if ARGV.include?("--reset")
rm -rf ./storage/{db,files} system "rm -rf ./storage/{db,files}"
rails db:migrate:reset system! "bin/rails db:reset"
else end
rails db:prepare system! "bin/rails db:prepare"
fi
announce "Cleaning up old logs, caches, and temporary files" puts "\n== Removing old logs and tempfiles =="
rails log:clear tmp:clear system! "bin/rails log:clear tmp:clear"
announce "Restarting services" puts "\n== Restarting services =="
rails restart system! "bin/rails restart"
end
if [ -d "$HOME/.puma-dev" ]; then
announce "Configuring puma-dev"
ln -nfs "$app_root" "$HOME/.puma-dev/$app_name"
announce "Checking that https://$app_name.test/up is live: "
curl -Is "https://$app_name.test/up" | head -n 1
fi

5
bin/thrust Executable file
View File

@@ -0,0 +1,5 @@
#!/usr/bin/env ruby
require "rubygems"
require "bundler/setup"
load Gem.bin_path("thruster", "thrust")

View File

@@ -7,7 +7,12 @@ Bundler.require(*Rails.groups)
module Campfire module Campfire
class Application < Rails::Application class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version. # Initialize configuration defaults for originally generated Rails version.
config.load_defaults 7.0 config.load_defaults 8.1
# Please, add to the `ignore` list any other `lib` subdirectories that do
# not contain `.rb` files, or that should not be reloaded or eager loaded.
# Common ones are `templates`, `generators`, or `middleware`, for example.
config.autoload_lib(ignore: %w[assets tasks rails_ext])
# Fallback to English if translation key is missing # Fallback to English if translation key is missing
config.i18n.fallbacks = true config.i18n.fallbacks = true

View File

@@ -3,13 +3,13 @@
{ {
"warning_type": "Dynamic Render Path", "warning_type": "Dynamic Render Path",
"warning_code": 15, "warning_code": 15,
"fingerprint": "3ba57fe811dd7154100cbd8ca7a262c5a316d50d0d44b877fec8dbfdf710cdd0", "fingerprint": "58c19ad06ce51511be9d6d9e5828f4a0ed96ab52c963ff37b4b4a606620ab66b",
"check_name": "Render", "check_name": "Render",
"message": "Render path contains parameter value", "message": "Render path contains parameter value",
"file": "app/views/messages/show.html.erb", "file": "app/views/messages/show.html.erb",
"line": 1, "line": 1,
"link": "https://brakemanscanner.org/docs/warning_types/dynamic_render_path/", "link": "https://brakemanscanner.org/docs/warning_types/dynamic_render_path/",
"code": "render(action => @room.messages.find(params[:id]), {})", "code": "render(action => @room.messages.find(params[:id]), { :locals => ({ :brakemanunresolvedmodel => @room.messages.find(params[:id]) }) })",
"render_path": [ "render_path": [
{ {
"type": "controller", "type": "controller",
@@ -33,31 +33,7 @@
22 22
], ],
"note": "" "note": ""
},
{
"warning_type": "File Access",
"warning_code": 16,
"fingerprint": "64b6a0c35632fbde1eaa8c5441b070fec379a983ac91997b5b48177d6d36b024",
"check_name": "SendFile",
"message": "Model attribute used in file name",
"file": "app/controllers/users/avatars_controller.rb",
"line": 14,
"link": "https://brakemanscanner.org/docs/warning_types/file_access/",
"code": "send_file(ActiveStorage::Blob.service.path_for(User.find(params[:user_id]).avatar.variant(:resize_to_limit => ([512, 512]), :format => :webp).processed.key), :content_type => \"image/webp\", :disposition => :inline)",
"render_path": null,
"location": {
"type": "method",
"class": "Users::AvatarsController",
"method": "show"
},
"user_input": "User.find(params[:user_id]).avatar",
"confidence": "Weak",
"cwe_id": [
22
],
"note": ""
} }
], ],
"updated": "2024-01-18 16:36:00 -0800", "brakeman_version": "7.1.0"
"brakeman_version": "6.1.1"
} }

5
config/bundler-audit.yml Normal file
View File

@@ -0,0 +1,5 @@
# Audit all gems listed in the Gemfile for known security problems by running bin/bundler-audit.
# CVEs that are not relevant to the application can be enumerated on the ignore list below.
ignore:
- CVE-THAT-DOES-NOT-APPLY

21
config/ci.rb Normal file
View File

@@ -0,0 +1,21 @@
# Run using bin/ci
CI.run do
step "Setup", "bin/setup --skip-server"
step "Style: Ruby", "bin/rubocop"
step "Security: Gem audit", "bin/bundler-audit"
step "Security: Importmap vulnerability audit", "bin/importmap audit"
step "Security: Brakeman code analysis", "bin/brakeman --quiet --no-pager --exit-on-warn --exit-on-error"
step "Tests: Rails", "bin/rails test"
step "Tests: System", "bin/rails test:system"
step "Tests: Seeds", "env RAILS_ENV=test bin/rails db:seed:replant"
if success?
step "Signoff: All systems go. Ready for merge and deploy.", "gh signoff"
else
failure "Signoff: CI failed. Do not merge or deploy.", "Fix the issues and try again."
end
end

View File

@@ -7,7 +7,7 @@
default: &default default: &default
adapter: sqlite3 adapter: sqlite3
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 10 } %> pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 10 } %>
retries: 100 timeout: 5000
default_transaction_mode: immediate default_transaction_mode: immediate
development: development:

View File

@@ -3,10 +3,8 @@ require "active_support/core_ext/integer/time"
Rails.application.configure do Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb. # Settings specified here will take precedence over those in config/application.rb.
# In the development environment your application's code is reloaded any time # Make code changes take effect immediately without server restart.
# it changes. This slows down response time but is perfect for development config.enable_reloading = true
# since you don't have to restart the web server when you make code changes.
config.cache_classes = false
# Do not eager load code on boot. # Do not eager load code on boot.
config.eager_load = false config.eager_load = false
@@ -14,18 +12,18 @@ Rails.application.configure do
# Show full error reports. # Show full error reports.
config.consider_all_requests_local = true config.consider_all_requests_local = true
# Enable server timing # Enable server timing.
config.server_timing = true config.server_timing = true
# Enable/disable caching. By default caching is disabled. # Enable/disable Action Controller caching. By default Action Controller caching is disabled.
# Run rails dev:cache to toggle caching. # Run rails dev:cache to toggle Action Controller caching.
if Rails.root.join("tmp/caching-dev.txt").exist? if Rails.root.join("tmp/caching-dev.txt").exist?
config.action_controller.perform_caching = true config.action_controller.perform_caching = true
config.action_controller.enable_fragment_cache_logging = true config.action_controller.enable_fragment_cache_logging = true
config.cache_store = :redis_cache_store config.cache_store = :redis_cache_store
config.public_file_server.headers = { config.public_file_server.headers = {
"Cache-Control" => "public, max-age=#{2.days.to_i}" "cache-control" => "public, max-age=#{2.days.to_i}"
} }
else else
config.action_controller.perform_caching = false config.action_controller.perform_caching = false
@@ -51,6 +49,9 @@ Rails.application.configure do
# Highlight code that triggered database queries in logs. # Highlight code that triggered database queries in logs.
config.active_record.verbose_query_logs = true config.active_record.verbose_query_logs = true
# Append comments with runtime information tags to SQL queries in logs.
config.active_record.query_log_tags_enabled = true
# Allow the app to be served from any host. # Allow the app to be served from any host.
config.hosts = [] config.hosts = []
@@ -66,6 +67,18 @@ Rails.application.configure do
# Uncomment to test with production class job queue # Uncomment to test with production class job queue
# config.active_job.queue_adapter = :resque # config.active_job.queue_adapter = :resque
# Highlight code that enqueued background job in logs.
config.active_job.verbose_enqueue_logs = true
# Suppress logger output for asset requests.
config.assets.quiet = true
# Annotate rendered view with file names.
config.action_view.annotate_rendered_view_with_filenames = true
# Raise error when a before_action's only/except options reference missing actions.
config.action_controller.raise_on_missing_callback_actions = true
# Visit /rails/locks to see the locks # Visit /rails/locks to see the locks
config.middleware.insert_before Rack::Sendfile, ActionDispatch::DebugLocks config.middleware.insert_before Rack::Sendfile, ActionDispatch::DebugLocks
end end

View File

@@ -5,18 +5,30 @@ Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb. # Settings specified here will take precedence over those in config/application.rb.
# Code is not reloaded between requests. # Code is not reloaded between requests.
config.cache_classes = true config.enable_reloading = false
# Eager load code on boot. This eager loads most of Rails and # Eager load code on boot for better performance and memory savings (ignored by Rake tasks).
# your application in memory, allowing both threaded web servers
# and those relying on copy on write to perform better.
# Rake tasks automatically ignore this option for performance.
config.eager_load = true config.eager_load = true
# Full error reports are disabled and caching is turned on. # Full error reports are disabled and caching is turned on.
config.consider_all_requests_local = false config.consider_all_requests_local = false
config.action_controller.perform_caching = true config.action_controller.perform_caching = true
# Cache digest stamped assets for far-future expiry.
# Short cache for others: robots.txt, sitemap.xml, 404.html, etc.
config.public_file_server.headers = {
"cache-control" => lambda do |path, _|
if path.start_with?("/assets/")
# Files in /assets/ are expected to be fully immutable.
# If the content change the URL too.
"public, immutable, max-age=#{1.year.to_i}"
else
# For anything else we cache for 1 minute.
"public, max-age=#{1.minute.to_i}, stale-while-revalidate=#{5.minutes.to_i}"
end
end
}
# Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
# or in config/master.key. This key is used to decrypt credentials (and other encrypted files). # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
# config.require_master_key = true # config.require_master_key = true
@@ -27,9 +39,15 @@ Rails.application.configure do
# Store uploaded files on the local file system (see config/storage.yml for options). # Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :local config.active_storage.service = :local
# Assume all access to the app is happening through a SSL-terminating reverse proxy.
# config.assume_ssl = true
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
# config.force_ssl = true # config.force_ssl = true
# Skip http-to-https redirect for the default health check endpoint.
# config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } }
# Log to STDOUT by default # Log to STDOUT by default
config.logger = ActiveSupport::Logger.new(STDOUT) config.logger = ActiveSupport::Logger.new(STDOUT)
.tap { |logger| logger.formatter = ::Logger::Formatter.new } .tap { |logger| logger.formatter = ::Logger::Formatter.new }
@@ -43,6 +61,12 @@ Rails.application.configure do
# for everything. # for everything.
config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info") config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info")
# Prevent health checks from clogging up the logs.
config.silence_healthcheck_path = "/up"
# Don't log any deprecations.
config.active_support.report_deprecations = false
# Cache in memory for now # Cache in memory for now
config.cache_store = :redis_cache_store config.cache_store = :redis_cache_store
@@ -65,8 +89,8 @@ Rails.application.configure do
# Do not dump schema after migrations. # Do not dump schema after migrations.
config.active_record.dump_schema_after_migration = false config.active_record.dump_schema_after_migration = false
# SQLite is good, actually # Only use :id for inspections in production.
config.active_record.sqlite3_production_warning = false config.active_record.attributes_for_inspect = [ :id ]
config.active_job.queue_adapter = :resque config.active_job.queue_adapter = :resque
end end

View File

@@ -8,18 +8,19 @@ require "active_support/core_ext/integer/time"
Rails.application.configure do Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb. # Settings specified here will take precedence over those in config/application.rb.
# Turn false under Spring and add config.action_view.cache_template_loading = true. # While tests run files are not watched, reloading is not necessary.
config.cache_classes = true config.enable_reloading = false
# Eager loading loads your whole application. When running a single test locally, # Eager loading loads your entire application. When running a single test locally,
# this probably isn't necessary. It's a good idea to do in a continuous integration # this is usually not necessary, and can slow down your test suite. However, it's
# system, or in some way before deploying your code. # recommended that you enable it in continuous integration systems to ensure eager
# loading is working properly before deploying your code.
config.eager_load = ENV["CI"].present? config.eager_load = ENV["CI"].present?
# Configure public file server for tests with Cache-Control for performance. # Configure public file server for tests with Cache-Control for performance.
config.public_file_server.enabled = true config.public_file_server.enabled = true
config.public_file_server.headers = { config.public_file_server.headers = {
"Cache-Control" => "public, max-age=#{1.hour.to_i}" "cache-control" => "public, max-age=#{1.hour.to_i}"
} }
# Show full error reports and disable caching. # Show full error reports and disable caching.
@@ -45,6 +46,9 @@ Rails.application.configure do
# Tell Active Support which deprecation messages to disallow. # Tell Active Support which deprecation messages to disallow.
config.active_support.disallowed_deprecation_warnings = [] config.active_support.disallowed_deprecation_warnings = []
# Raise error when a before_action's only/except options reference missing actions.
config.action_controller.raise_on_missing_callback_actions = true
# Raises error for missing translations. # Raises error for missing translations.
# config.i18n.raise_on_missing_translations = true # config.i18n.raise_on_missing_translations = true

View File

@@ -4,5 +4,5 @@
# sensitive information. See the ActiveSupport::ParameterFilter documentation for supported # sensitive information. See the ActiveSupport::ParameterFilter documentation for supported
# notations and behaviors. # notations and behaviors.
Rails.application.config.filter_parameters += [ Rails.application.config.filter_parameters += [
:passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn, :endpoint, "message.body" :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn, :cvv, :cvc, :endpoint, "message.body"
] ]

View File

@@ -11,6 +11,6 @@
# end # end
# These inflection rules are supported but not enabled by default: # These inflection rules are supported but not enabled by default:
# ActiveSupport::Inflector.inflections(:en) do |inflect| ActiveSupport::Inflector.inflections(:en) do |inflect|
# inflect.acronym "RESTful" inflect.acronym "HTTP"
# end end

View File

@@ -1,36 +0,0 @@
module SQLite3Configuration
private
def configure_connection
super
if @config[:retries]
retries = self.class.type_cast_config_to_integer(@config[:retries])
raw_connection.busy_handler do |count|
(count <= retries).tap { |result| sleep count * 0.001 if result }
end
end
end
end
module SQLite3DumpConfiguration
def structure_dump(filename, extra_flags)
args = []
args.concat(Array(extra_flags)) if extra_flags
args << db_config.database
ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
if ignore_tables.any?
ignore_tables = connection.data_sources.select { |table| ignore_tables.any? { |pattern| pattern === table } }
condition = ignore_tables.map { |table| connection.quote(table) }.join(", ")
args << "SELECT sql || ';' FROM sqlite_master WHERE tbl_name NOT IN (#{condition}) ORDER BY tbl_name, type DESC, name"
else
args << ".schema --nosys"
end
run_cmd("sqlite3", args, filename)
end
end
ActiveSupport.on_load :active_record do
ActiveRecord::ConnectionAdapters::SQLite3Adapter.prepend SQLite3Configuration
ActiveRecord::Tasks::SQLiteDatabaseTasks.prepend SQLite3DumpConfiguration
end

View File

@@ -0,0 +1,22 @@
# This migration comes from active_storage (originally 20190112182829)
class AddServiceNameToActiveStorageBlobs < ActiveRecord::Migration[6.0]
def up
return unless table_exists?(:active_storage_blobs)
unless column_exists?(:active_storage_blobs, :service_name)
add_column :active_storage_blobs, :service_name, :string
if configured_service = ActiveStorage::Blob.service.name
ActiveStorage::Blob.unscoped.update_all(service_name: configured_service)
end
change_column :active_storage_blobs, :service_name, :string, null: false
end
end
def down
return unless table_exists?(:active_storage_blobs)
remove_column :active_storage_blobs, :service_name
end
end

View File

@@ -0,0 +1,27 @@
# This migration comes from active_storage (originally 20191206030411)
class CreateActiveStorageVariantRecords < ActiveRecord::Migration[6.0]
def change
return unless table_exists?(:active_storage_blobs)
# Use Active Record's configured type for primary key
create_table :active_storage_variant_records, id: primary_key_type, if_not_exists: true do |t|
t.belongs_to :blob, null: false, index: false, type: blobs_primary_key_type
t.string :variation_digest, null: false
t.index %i[ blob_id variation_digest ], name: "index_active_storage_variant_records_uniqueness", unique: true
t.foreign_key :active_storage_blobs, column: :blob_id
end
end
private
def primary_key_type
config = Rails.configuration.generators
config.options[config.orm][:primary_key_type] || :primary_key
end
def blobs_primary_key_type
pkey_name = connection.primary_key(:active_storage_blobs)
pkey_column = connection.columns(:active_storage_blobs).find { |c| c.name == pkey_name }
pkey_column.bigint? ? :bigint : pkey_column.type
end
end

View File

@@ -0,0 +1,8 @@
# This migration comes from active_storage (originally 20211119233751)
class RemoveNotNullOnActiveStorageBlobsChecksum < ActiveRecord::Migration[6.0]
def change
return unless table_exists?(:active_storage_blobs)
change_column_null(:active_storage_blobs, :checksum, true)
end
end

View File

@@ -6,8 +6,6 @@ FOREIGN KEY ("user_id")
); );
CREATE INDEX "index_push_subscriptions_on_user_id" ON "push_subscriptions" ("user_id"); CREATE INDEX "index_push_subscriptions_on_user_id" ON "push_subscriptions" ("user_id");
CREATE INDEX "idx_on_endpoint_p256dh_key_auth_key_7553014576" ON "push_subscriptions" ("endpoint", "p256dh_key", "auth_key"); CREATE INDEX "idx_on_endpoint_p256dh_key_auth_key_7553014576" ON "push_subscriptions" ("endpoint", "p256dh_key", "auth_key");
CREATE TABLE IF NOT EXISTS "active_storage_blobs" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "key" varchar NOT NULL, "filename" varchar NOT NULL, "content_type" varchar, "metadata" text, "service_name" varchar NOT NULL, "byte_size" bigint NOT NULL, "checksum" varchar, "created_at" datetime(6) NOT NULL);
CREATE UNIQUE INDEX "index_active_storage_blobs_on_key" ON "active_storage_blobs" ("key");
CREATE TABLE IF NOT EXISTS "active_storage_attachments" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar NOT NULL, "record_type" varchar NOT NULL, "record_id" bigint NOT NULL, "blob_id" bigint NOT NULL, "created_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_c3b3935057" CREATE TABLE IF NOT EXISTS "active_storage_attachments" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar NOT NULL, "record_type" varchar NOT NULL, "record_id" bigint NOT NULL, "blob_id" bigint NOT NULL, "created_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_c3b3935057"
FOREIGN KEY ("blob_id") FOREIGN KEY ("blob_id")
REFERENCES "active_storage_blobs" ("id") REFERENCES "active_storage_blobs" ("id")
@@ -69,7 +67,12 @@ CREATE INDEX "index_webhooks_on_user_id" ON "webhooks" ("user_id");
CREATE TABLE IF NOT EXISTS "users" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "role" integer DEFAULT 0 NOT NULL, "email_address" varchar DEFAULT NULL, "password_digest" varchar DEFAULT NULL, "active" boolean DEFAULT 1, "bio" text DEFAULT NULL, "bot_token" varchar DEFAULT NULL); CREATE TABLE IF NOT EXISTS "users" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "role" integer DEFAULT 0 NOT NULL, "email_address" varchar DEFAULT NULL, "password_digest" varchar DEFAULT NULL, "active" boolean DEFAULT 1, "bio" text DEFAULT NULL, "bot_token" varchar DEFAULT NULL);
CREATE UNIQUE INDEX "index_users_on_email_address" ON "users" ("email_address"); CREATE UNIQUE INDEX "index_users_on_email_address" ON "users" ("email_address");
CREATE UNIQUE INDEX "index_users_on_bot_token" ON "users" ("bot_token"); CREATE UNIQUE INDEX "index_users_on_bot_token" ON "users" ("bot_token");
CREATE TABLE IF NOT EXISTS "active_storage_blobs" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "key" varchar NOT NULL, "filename" varchar NOT NULL, "content_type" varchar, "metadata" text, "service_name" varchar NOT NULL, "byte_size" bigint NOT NULL, "checksum" varchar, "created_at" datetime(6) NOT NULL);
CREATE UNIQUE INDEX "index_active_storage_blobs_on_key" ON "active_storage_blobs" ("key") /*application='Campfire'*/;
INSERT INTO "schema_migrations" (version) VALUES INSERT INTO "schema_migrations" (version) VALUES
('20250825100959'),
('20250825100958'),
('20250825100957'),
('20240209110503'), ('20240209110503'),
('20240131105830'), ('20240131105830'),
('20240130213001'), ('20240130213001'),

View File

@@ -13,7 +13,13 @@ ActiveSupport.on_load(:action_text_content) do
def attachable_from_possibly_expired_sgid(sgid) def attachable_from_possibly_expired_sgid(sgid)
if message = sgid&.split("--")&.first if message = sgid&.split("--")&.first
encoded_message = JSON.parse Base64.strict_decode64(message) encoded_message = JSON.parse Base64.strict_decode64(message)
decoded_gid = Marshal.load Base64.urlsafe_decode64(encoded_message.dig("_rails", "message"))
decoded_gid = if data = encoded_message.dig("_rails", "data")
data
else
nil
end
model = GlobalID.find(decoded_gid) model = GlobalID.find(decoded_gid)
model.model_name.to_s.in?(ATTACHABLES_PERMITTED_WITH_INVALID_SIGNATURES) ? model : nil model.model_name.to_s.in?(ATTACHABLES_PERMITTED_WITH_INVALID_SIGNATURES) ? model : nil

View File

@@ -7,6 +7,6 @@ task "resque:pool:setup" do
Resque::Pool.after_prefork do |job| Resque::Pool.after_prefork do |job|
ActiveRecord::Base.establish_connection ActiveRecord::Base.establish_connection
Resque.redis.client.reconnect Resque.redis.client.close
end end
end end

View File

@@ -7,14 +7,14 @@ class Messages::ByBotsControlleTest < ActionDispatch::IntegrationTest
test "create" do test "create" do
assert_difference -> { Message.count }, +1 do assert_difference -> { Message.count }, +1 do
post room_bot_messages_url(@room, users(:bender).bot_key), params: "Hello Bot World!" post room_bot_messages_url(@room, users(:bender).bot_key), params: +"Hello Bot World!"
assert_equal "Hello Bot World!", Message.last.plain_text_body assert_equal "Hello Bot World!", Message.last.plain_text_body
end end
end end
test "create with UTF-8 content" do test "create with UTF-8 content" do
assert_difference -> { Message.count }, +1 do assert_difference -> { Message.count }, +1 do
post room_bot_messages_url(@room, users(:bender).bot_key), params: "Hello 👋!" post room_bot_messages_url(@room, users(:bender).bot_key), params: +"Hello 👋!"
assert_equal "Hello 👋!", Message.last.plain_text_body assert_equal "Hello 👋!", Message.last.plain_text_body
end end
end end
@@ -36,7 +36,7 @@ class Messages::ByBotsControlleTest < ActionDispatch::IntegrationTest
test "create does not trigger a webhook to the sending bot in a direct room" do test "create does not trigger a webhook to the sending bot in a direct room" do
assert_no_enqueued_jobs only: Bot::WebhookJob do assert_no_enqueued_jobs only: Bot::WebhookJob do
post room_bot_messages_url(rooms(:bender_and_kevin), users(:bender).bot_key), params: "Talking to myself again!" post room_bot_messages_url(rooms(:bender_and_kevin), users(:bender).bot_key), params: +"Talking to myself again!"
end end
end end

View File

@@ -122,8 +122,8 @@ class Opengraph::MetadataTest < ActiveSupport::TestCase
metadata = Opengraph::Metadata.from_url("https://www.example.com") metadata = Opengraph::Metadata.from_url("https://www.example.com")
assert metadata.valid? assert metadata.valid?
assert_equal "Hey!", metadata.title assert_equal "Hey!alert('hi')", metadata.title
assert_equal "Hello", metadata.description assert_equal "Helloalert('hi')", metadata.description
end end
test "remove encoded tags from title and description" do test "remove encoded tags from title and description" do

View File

@@ -9,11 +9,9 @@ require "webmock/minitest"
WebMock.enable! WebMock.enable!
class ActiveSupport::TestCase class ActiveSupport::TestCase
# FIXME: Why isn't this included in ActiveSupport::TestCase by default? include ActiveJob::TestHelper
include ActiveJob::TestHelper, Turbo::Broadcastable::TestHelper
# FIXME: sqlite3 isn't correctly creating the additional databases per core parallelize(workers: :number_of_processors)
# parallelize(workers: :number_of_processors)
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
fixtures :all fixtures :all