diff --git a/.dockerignore b/.dockerignore index 325bfc0..cd7190b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -7,8 +7,9 @@ # Ignore bundler config. /.bundle -# Ignore all environment files. +# Ignore all environment files (except templates). /.env* +!/.env*.erb # Ignore all default key files. /config/master.key @@ -39,10 +40,6 @@ # Ignore CI service files. /.github -# Ignore Kamal files. -/config/deploy*.yml -/.kamal - # Ignore development files /.devcontainer diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 83610cf..f0527e6 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,10 +3,10 @@ updates: - package-ecosystem: bundler directory: "/" schedule: - interval: weekly + interval: daily open-pull-requests-limit: 10 - package-ecosystem: github-actions directory: "/" schedule: - interval: weekly + interval: daily open-pull-requests-limit: 10 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1a34dc2..038861c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,29 +11,28 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@v4 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: + ruby-version: .ruby-version bundler-cache: true - name: Scan for common Rails security vulnerabilities using static analysis run: bin/brakeman --no-pager - - - name: Scan for known security vulnerabilities in gems used - run: bin/bundler-audit - + scan_js: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@v4 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: + ruby-version: .ruby-version bundler-cache: true - name: Scan for security vulnerabilities in JavaScript dependencies @@ -41,85 +40,57 @@ jobs: lint: runs-on: ubuntu-latest - env: - RUBOCOP_CACHE_ROOT: tmp/rubocop steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@v4 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: + ruby-version: .ruby-version bundler-cache: true - - name: Prepare RuboCop cache - uses: actions/cache@v4 - env: - DEPENDENCIES_HASH: ${{ hashFiles('.ruby-version', '**/.rubocop.yml', '**/.rubocop_todo.yml', 'Gemfile.lock') }} - with: - path: ${{ env.RUBOCOP_CACHE_ROOT }} - key: rubocop-${{ runner.os }}-${{ env.DEPENDENCIES_HASH }}-${{ github.ref_name == github.event.repository.default_branch && github.run_id || 'default' }} - restore-keys: | - rubocop-${{ runner.os }}-${{ env.DEPENDENCIES_HASH }}- - - name: Lint code for consistent style run: bin/rubocop -f github test: runs-on: ubuntu-latest - # services: - # redis: - # image: valkey/valkey:8 - # ports: - # - 6379:6379 - # options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5 - steps: - - name: Install packages - run: sudo apt-get update && sudo apt-get install --no-install-recommends -y libvips - - - name: Checkout code - uses: actions/checkout@v6 - - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - bundler-cache: true - - - name: Run tests + services: + postgres: + image: postgres env: - RAILS_ENV: test - # RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }} - # REDIS_URL: redis://localhost:6379/0 - run: bin/rails db:test:prepare test + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + ports: + - 5432:5432 + options: --health-cmd="pg_isready" --health-interval=10s --health-timeout=5s --health-retries=3 + + # redis: + # image: redis + # ports: + # - 6379:6379 + # options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5 - system-test: - runs-on: ubuntu-latest - - # services: - # redis: - # image: valkey/valkey:8 - # ports: - # - 6379:6379 - # options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5 steps: - name: Install packages - run: sudo apt-get update && sudo apt-get install --no-install-recommends -y libvips + run: sudo apt-get update && sudo apt-get install --no-install-recommends -y google-chrome-stable curl libjemalloc2 libvips postgresql-client - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@v4 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: + ruby-version: .ruby-version bundler-cache: true - - name: Run System Tests + - name: Run tests env: RAILS_ENV: test - # RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }} + DATABASE_URL: postgres://postgres:postgres@localhost:5432 # REDIS_URL: redis://localhost:6379/0 - run: bin/rails db:test:prepare test:system + run: bin/rails db:test:prepare test test:system - name: Keep screenshots from failed system tests uses: actions/upload-artifact@v4 diff --git a/.gitignore b/.gitignore index 29fe215..4aaf102 100644 --- a/.gitignore +++ b/.gitignore @@ -7,8 +7,9 @@ # Ignore bundler config. /.bundle -# Ignore all environment files. +# Ignore all environment files (except templates). /.env* +!/.env*.erb # Ignore all logfiles and tempfiles. /log/* @@ -30,10 +31,5 @@ /public/assets -# Ignore key files for decrypting credentials and more. -/config/*.key - - -/app/assets/builds/* -!/app/assets/builds/.keep -/.idea +# Ignore master key for decrypting credentials and more. +/config/master.key diff --git a/.kamal/hooks/docker-setup.sample b/.kamal/hooks/docker-setup.sample deleted file mode 100755 index 2fb07d7..0000000 --- a/.kamal/hooks/docker-setup.sample +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -echo "Docker set up on $KAMAL_HOSTS..." diff --git a/.kamal/hooks/post-app-boot.sample b/.kamal/hooks/post-app-boot.sample deleted file mode 100755 index 70f9c4b..0000000 --- a/.kamal/hooks/post-app-boot.sample +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -echo "Booted app version $KAMAL_VERSION on $KAMAL_HOSTS..." diff --git a/.kamal/hooks/post-deploy.sample b/.kamal/hooks/post-deploy.sample deleted file mode 100755 index fd364c2..0000000 --- a/.kamal/hooks/post-deploy.sample +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh - -# A sample post-deploy hook -# -# These environment variables are available: -# KAMAL_RECORDED_AT -# KAMAL_PERFORMER -# KAMAL_VERSION -# KAMAL_HOSTS -# KAMAL_ROLES (if set) -# KAMAL_DESTINATION (if set) -# KAMAL_RUNTIME - -echo "$KAMAL_PERFORMER deployed $KAMAL_VERSION to $KAMAL_DESTINATION in $KAMAL_RUNTIME seconds" diff --git a/.kamal/hooks/post-proxy-reboot.sample b/.kamal/hooks/post-proxy-reboot.sample deleted file mode 100755 index 1435a67..0000000 --- a/.kamal/hooks/post-proxy-reboot.sample +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -echo "Rebooted kamal-proxy on $KAMAL_HOSTS" diff --git a/.kamal/hooks/pre-app-boot.sample b/.kamal/hooks/pre-app-boot.sample deleted file mode 100755 index 45f7355..0000000 --- a/.kamal/hooks/pre-app-boot.sample +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -echo "Booting app version $KAMAL_VERSION on $KAMAL_HOSTS..." diff --git a/.kamal/hooks/pre-build.sample b/.kamal/hooks/pre-build.sample deleted file mode 100755 index c5a5567..0000000 --- a/.kamal/hooks/pre-build.sample +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/sh - -# A sample pre-build hook -# -# Checks: -# 1. We have a clean checkout -# 2. A remote is configured -# 3. The branch has been pushed to the remote -# 4. The version we are deploying matches the remote -# -# These environment variables are available: -# KAMAL_RECORDED_AT -# KAMAL_PERFORMER -# KAMAL_VERSION -# KAMAL_HOSTS -# KAMAL_ROLES (if set) -# KAMAL_DESTINATION (if set) - -if [ -n "$(git status --porcelain)" ]; then - echo "Git checkout is not clean, aborting..." >&2 - git status --porcelain >&2 - exit 1 -fi - -first_remote=$(git remote) - -if [ -z "$first_remote" ]; then - echo "No git remote set, aborting..." >&2 - exit 1 -fi - -current_branch=$(git branch --show-current) - -if [ -z "$current_branch" ]; then - echo "Not on a git branch, aborting..." >&2 - exit 1 -fi - -remote_head=$(git ls-remote $first_remote --tags $current_branch | cut -f1) - -if [ -z "$remote_head" ]; then - echo "Branch not pushed to remote, aborting..." >&2 - exit 1 -fi - -if [ "$KAMAL_VERSION" != "$remote_head" ]; then - echo "Version ($KAMAL_VERSION) does not match remote HEAD ($remote_head), aborting..." >&2 - exit 1 -fi - -exit 0 diff --git a/.kamal/hooks/pre-connect.sample b/.kamal/hooks/pre-connect.sample deleted file mode 100755 index 77744bd..0000000 --- a/.kamal/hooks/pre-connect.sample +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env ruby - -# A sample pre-connect check -# -# Warms DNS before connecting to hosts in parallel -# -# These environment variables are available: -# KAMAL_RECORDED_AT -# KAMAL_PERFORMER -# KAMAL_VERSION -# KAMAL_HOSTS -# KAMAL_ROLES (if set) -# KAMAL_DESTINATION (if set) -# KAMAL_RUNTIME - -hosts = ENV["KAMAL_HOSTS"].split(",") -results = nil -max = 3 - -elapsed = Benchmark.realtime do - results = hosts.map do |host| - Thread.new do - tries = 1 - - begin - Socket.getaddrinfo(host, 0, Socket::AF_UNSPEC, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME) - rescue SocketError - if tries < max - puts "Retrying DNS warmup: #{host}" - tries += 1 - sleep rand - retry - else - puts "DNS warmup failed: #{host}" - host - end - end - - tries - end - end.map(&:value) -end - -retries = results.sum - hosts.size -nopes = results.count { |r| r == max } - -puts "Prewarmed %d DNS lookups in %.2f sec: %d retries, %d failures" % [ hosts.size, elapsed, retries, nopes ] diff --git a/.kamal/hooks/pre-deploy.sample b/.kamal/hooks/pre-deploy.sample deleted file mode 100755 index 05b3055..0000000 --- a/.kamal/hooks/pre-deploy.sample +++ /dev/null @@ -1,122 +0,0 @@ -#!/usr/bin/env ruby - -# A sample pre-deploy hook -# -# Checks the Github status of the build, waiting for a pending build to complete for up to 720 seconds. -# -# Fails unless the combined status is "success" -# -# These environment variables are available: -# KAMAL_RECORDED_AT -# KAMAL_PERFORMER -# KAMAL_VERSION -# KAMAL_HOSTS -# KAMAL_COMMAND -# KAMAL_SUBCOMMAND -# KAMAL_ROLES (if set) -# KAMAL_DESTINATION (if set) - -# Only check the build status for production deployments -if ENV["KAMAL_COMMAND"] == "rollback" || ENV["KAMAL_DESTINATION"] != "production" - exit 0 -end - -require "bundler/inline" - -# true = install gems so this is fast on repeat invocations -gemfile(true, quiet: true) do - source "https://rubygems.org" - - gem "octokit" - gem "faraday-retry" -end - -MAX_ATTEMPTS = 72 -ATTEMPTS_GAP = 10 - -def exit_with_error(message) - $stderr.puts message - exit 1 -end - -class GithubStatusChecks - attr_reader :remote_url, :git_sha, :github_client, :combined_status - - def initialize - @remote_url = github_repo_from_remote_url - @git_sha = `git rev-parse HEAD`.strip - @github_client = Octokit::Client.new(access_token: ENV["GITHUB_TOKEN"]) - refresh! - end - - def refresh! - @combined_status = github_client.combined_status(remote_url, git_sha) - end - - def state - combined_status[:state] - end - - def first_status_url - first_status = combined_status[:statuses].find { |status| status[:state] == state } - first_status && first_status[:target_url] - end - - def complete_count - combined_status[:statuses].count { |status| status[:state] != "pending"} - end - - def total_count - combined_status[:statuses].count - end - - def current_status - if total_count > 0 - "Completed #{complete_count}/#{total_count} checks, see #{first_status_url} ..." - else - "Build not started..." - end - end - - private - def github_repo_from_remote_url - url = `git config --get remote.origin.url`.strip.delete_suffix(".git") - if url.start_with?("https://github.com/") - url.delete_prefix("https://github.com/") - elsif url.start_with?("git@github.com:") - url.delete_prefix("git@github.com:") - else - url - end - end -end - - -$stdout.sync = true - -begin - puts "Checking build status..." - - attempts = 0 - checks = GithubStatusChecks.new - - loop do - case checks.state - when "success" - puts "Checks passed, see #{checks.first_status_url}" - exit 0 - when "failure" - exit_with_error "Checks failed, see #{checks.first_status_url}" - when "pending" - attempts += 1 - end - - exit_with_error "Checks are still pending, gave up after #{MAX_ATTEMPTS * ATTEMPTS_GAP} seconds" if attempts == MAX_ATTEMPTS - - puts checks.current_status - sleep(ATTEMPTS_GAP) - checks.refresh! - end -rescue Octokit::NotFound - exit_with_error "Build status could not be found" -end diff --git a/.kamal/hooks/pre-proxy-reboot.sample b/.kamal/hooks/pre-proxy-reboot.sample deleted file mode 100755 index 061f805..0000000 --- a/.kamal/hooks/pre-proxy-reboot.sample +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -echo "Rebooting kamal-proxy on $KAMAL_HOSTS..." diff --git a/.kamal/secrets b/.kamal/secrets deleted file mode 100644 index b3089d6..0000000 --- a/.kamal/secrets +++ /dev/null @@ -1,20 +0,0 @@ -# Secrets defined here are available for reference under registry/password, env/secret, builder/secrets, -# and accessories/*/env/secret in config/deploy.yml. All secrets should be pulled from either -# password manager, ENV, or a file. DO NOT ENTER RAW CREDENTIALS HERE! This file needs to be safe for git. - -# Example of extracting secrets from 1password (or another compatible pw manager) -# SECRETS=$(kamal secrets fetch --adapter 1password --account your-account --from Vault/Item KAMAL_REGISTRY_PASSWORD RAILS_MASTER_KEY) -# KAMAL_REGISTRY_PASSWORD=$(kamal secrets extract KAMAL_REGISTRY_PASSWORD ${SECRETS}) -# RAILS_MASTER_KEY=$(kamal secrets extract RAILS_MASTER_KEY ${SECRETS}) - -# Example of extracting secrets from Rails credentials -# KAMAL_REGISTRY_PASSWORD=$(rails credentials:fetch kamal.registry_password) - -# Use a GITHUB_TOKEN if private repositories are needed for the image -# GITHUB_TOKEN=$(gh config get -h github.com oauth_token) - -# Grab the registry password from ENV -# KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD - -# Improve security by using a password manager. Never check config/master.key into git! -RAILS_MASTER_KEY=$(cat config/master.key) diff --git a/.ruby-version b/.ruby-version index 9c63baa..1454f6e 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -ruby-4.0.2 +4.0.1 diff --git a/Dockerfile b/Dockerfile index 7279a8c..54e905b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,11 @@ -# syntax=docker/dockerfile:1 -# check=error=true +# syntax = docker/dockerfile:1 # This Dockerfile is designed for production, not development. Use with Kamal or build'n'run by hand: -# docker build -t community_foundations . -# docker run -d -p 80:80 -e RAILS_MASTER_KEY= --name community_foundations community_foundations - -# For a containerized dev environment, see Dev Containers: https://guides.rubyonrails.org/getting_started_with_devcontainer.html +# docker build -t my-app . +# docker run -d -p 80:80 -p 443:443 --name my-app -e RAILS_MASTER_KEY= my-app # Make sure RUBY_VERSION matches the Ruby version in .ruby-version -ARG RUBY_VERSION=4.0.2 +ARG RUBY_VERSION=3.1.2 FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base # Rails app lives here @@ -16,40 +13,34 @@ WORKDIR /rails # Install base packages RUN apt-get update -qq && \ - apt-get install --no-install-recommends -y curl libjemalloc2 libvips sqlite3 && \ - ln -s /usr/lib/$(uname -m)-linux-gnu/libjemalloc.so.2 /usr/local/lib/libjemalloc.so && \ + apt-get install --no-install-recommends -y curl libjemalloc2 libvips postgresql-client && \ rm -rf /var/lib/apt/lists /var/cache/apt/archives -# Set production environment variables and enable jemalloc for reduced memory usage and latency. +# Set production environment ENV RAILS_ENV="production" \ BUNDLE_DEPLOYMENT="1" \ BUNDLE_PATH="/usr/local/bundle" \ - BUNDLE_WITHOUT="development" \ - LD_PRELOAD="/usr/local/lib/libjemalloc.so" + BUNDLE_WITHOUT="development" # Throw-away build stage to reduce size of final image FROM base AS build # Install packages needed to build gems RUN apt-get update -qq && \ - apt-get install --no-install-recommends -y build-essential git libvips libyaml-dev pkg-config && \ + apt-get install --no-install-recommends -y build-essential git libpq-dev libyaml-dev pkg-config && \ rm -rf /var/lib/apt/lists /var/cache/apt/archives # Install application gems -COPY vendor/* ./vendor/ COPY Gemfile Gemfile.lock ./ - RUN bundle install && \ rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \ - # -j 1 disable parallel compilation to avoid a QEMU bug: https://github.com/rails/bootsnap/issues/495 - bundle exec bootsnap precompile -j 1 --gemfile + bundle exec bootsnap precompile --gemfile # Copy application code COPY . . -# Precompile bootsnap code for faster boot times. -# -j 1 disable parallel compilation to avoid a QEMU bug: https://github.com/rails/bootsnap/issues/495 -RUN bundle exec bootsnap precompile -j 1 app/ lib/ +# Precompile bootsnap code for faster boot times +RUN bundle exec bootsnap precompile app/ lib/ # Precompiling assets for production without requiring secret RAILS_MASTER_KEY RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile @@ -60,18 +51,19 @@ RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile # Final stage for app image FROM base +# Copy built artifacts: gems, application +COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}" +COPY --from=build /rails /rails + # 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 + useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \ + chown -R rails:rails db log storage tmp USER 1000:1000 -# Copy built artifacts: gems, application -COPY --chown=rails:rails --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}" -COPY --chown=rails:rails --from=build /rails /rails - # Entrypoint prepares the database. ENTRYPOINT ["/rails/bin/docker-entrypoint"] -# Start server via Thruster by default, this can be overwritten at runtime -EXPOSE 80 -CMD ["./bin/thrust", "./bin/rails", "server"] +# Start the server by default, this can be overwritten at runtime +EXPOSE 3000 +CMD ["./bin/rails", "server"] diff --git a/Gemfile b/Gemfile index 116b9bc..96834c2 100644 --- a/Gemfile +++ b/Gemfile @@ -1,11 +1,11 @@ source "https://rubygems.org" -# Use specific branch of Rails -gem "rails", github: "rails/rails", branch: "8-1-stable" -# The modern asset pipeline for Rails [https://github.com/rails/propshaft] -gem "propshaft" -# Use sqlite3 as the database for Active Record -gem "sqlite3", ">= 2.1" +# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main" +gem "rails", "~> 8.0" +# The original asset pipeline for Rails [https://github.com/rails/sprockets-rails] +gem "sprockets-rails" +# Use postgresql as the database for Active Record +gem "pg", "~> 1.1" # Use the Puma web server [https://github.com/puma/puma] gem "puma", ">= 5.0" # Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails] @@ -14,40 +14,29 @@ gem "importmap-rails" gem "turbo-rails" # Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev] gem "stimulus-rails" -# Use Tailwind CSS [https://github.com/rails/tailwindcss-rails] -gem "tailwindcss-rails" # Build JSON APIs with ease [https://github.com/rails/jbuilder] gem "jbuilder" +# Use Redis adapter to run Action Cable in production +# gem "redis", ">= 4.0.1" + +# Use Kredis to get higher-level data types in Redis [https://github.com/rails/kredis] +# gem "kredis" # Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword] -gem "bcrypt", "~> 3.1.7" +# gem "bcrypt", "~> 3.1.7" # Windows does not include zoneinfo files, so bundle the tzinfo-data gem -gem "tzinfo-data", platforms: %i[ windows jruby ] - -# Use the database-backed adapters for Rails.cache, Active Job, and Action Cable -gem "solid_cache" -gem "solid_queue" -gem "solid_cable" +gem "tzinfo-data", platforms: %i[ mswin mswin64 mingw x64_mingw jruby ] # Reduces boot times through caching; required in config/boot.rb gem "bootsnap", require: false -# Deploy this application anywhere as a Docker container [https://kamal-deploy.org] -gem "kamal", require: false - -# Add HTTP asset caching/compression and X-Sendfile acceleration to Puma [https://github.com/basecamp/thruster/] -gem "thruster", require: false - # Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images] -gem "image_processing", "~> 1.2" +# gem "image_processing", "~> 1.2" group :development, :test do # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem - gem "debug", platforms: %i[ mri windows ], require: "debug/prelude" - - # Audits gems for known security defects (use config/bundler-audit.yml to ignore issues) - gem "bundler-audit", require: false + gem "debug", platforms: %i[ mri mswin mswin64 mingw x64_mingw ], require: "debug/prelude" # Static analysis for security vulnerabilities [https://brakemanscanner.org/] gem "brakeman", require: false @@ -60,8 +49,8 @@ group :development do # Use console on exceptions pages [https://github.com/rails/web-console] gem "web-console" - # Open sent emails in the browser instead of delivering them [https://github.com/ryanb/letter_opener] - gem "letter_opener" + # Highlight the fine-grained location where an error occurred [https://github.com/ruby/error_highlight] + gem "error_highlight", ">= 0.4.0", platforms: [ :ruby ] end group :test do diff --git a/Gemfile.lock b/Gemfile.lock index 6cd87a5..0fe0415 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,31 +1,31 @@ -GIT - remote: https://github.com/rails/rails.git - revision: 989fb54a6971328bd229e88b67a47223eb73cd76 - branch: 8-1-stable +GEM + remote: https://rubygems.org/ specs: - actioncable (8.1.3) - actionpack (= 8.1.3) - activesupport (= 8.1.3) + action_text-trix (2.1.16) + railties + actioncable (8.1.2) + actionpack (= 8.1.2) + activesupport (= 8.1.2) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (8.1.3) - actionpack (= 8.1.3) - activejob (= 8.1.3) - activerecord (= 8.1.3) - activestorage (= 8.1.3) - activesupport (= 8.1.3) + actionmailbox (8.1.2) + actionpack (= 8.1.2) + activejob (= 8.1.2) + activerecord (= 8.1.2) + activestorage (= 8.1.2) + activesupport (= 8.1.2) mail (>= 2.8.0) - actionmailer (8.1.3) - actionpack (= 8.1.3) - actionview (= 8.1.3) - activejob (= 8.1.3) - activesupport (= 8.1.3) + actionmailer (8.1.2) + actionpack (= 8.1.2) + actionview (= 8.1.2) + activejob (= 8.1.2) + activesupport (= 8.1.2) mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (8.1.3) - actionview (= 8.1.3) - activesupport (= 8.1.3) + actionpack (8.1.2) + actionview (= 8.1.2) + activesupport (= 8.1.2) nokogiri (>= 1.8.5) rack (>= 2.2.4) rack-session (>= 1.0.1) @@ -33,36 +33,36 @@ GIT rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) useragent (~> 0.16) - actiontext (8.1.3) + actiontext (8.1.2) action_text-trix (~> 2.1.15) - actionpack (= 8.1.3) - activerecord (= 8.1.3) - activestorage (= 8.1.3) - activesupport (= 8.1.3) + actionpack (= 8.1.2) + activerecord (= 8.1.2) + activestorage (= 8.1.2) + activesupport (= 8.1.2) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (8.1.3) - activesupport (= 8.1.3) + actionview (8.1.2) + activesupport (= 8.1.2) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (8.1.3) - activesupport (= 8.1.3) + activejob (8.1.2) + activesupport (= 8.1.2) globalid (>= 0.3.6) - activemodel (8.1.3) - activesupport (= 8.1.3) - activerecord (8.1.3) - activemodel (= 8.1.3) - activesupport (= 8.1.3) + activemodel (8.1.2) + activesupport (= 8.1.2) + activerecord (8.1.2) + activemodel (= 8.1.2) + activesupport (= 8.1.2) timeout (>= 0.4.0) - activestorage (8.1.3) - actionpack (= 8.1.3) - activejob (= 8.1.3) - activerecord (= 8.1.3) - activesupport (= 8.1.3) + activestorage (8.1.2) + actionpack (= 8.1.2) + activejob (= 8.1.2) + activerecord (= 8.1.2) + activesupport (= 8.1.2) marcel (~> 1.0) - activesupport (8.1.3) + activesupport (8.1.2) base64 bigdecimal concurrent-ruby (~> 1.0, >= 1.3.1) @@ -75,51 +75,17 @@ GIT securerandom (>= 0.3) tzinfo (~> 2.0, >= 2.0.5) uri (>= 0.13.1) - rails (8.1.3) - actioncable (= 8.1.3) - actionmailbox (= 8.1.3) - actionmailer (= 8.1.3) - actionpack (= 8.1.3) - actiontext (= 8.1.3) - actionview (= 8.1.3) - activejob (= 8.1.3) - activemodel (= 8.1.3) - activerecord (= 8.1.3) - activestorage (= 8.1.3) - activesupport (= 8.1.3) - bundler (>= 1.15.0) - railties (= 8.1.3) - railties (8.1.3) - actionpack (= 8.1.3) - activesupport (= 8.1.3) - irb (~> 1.13) - rackup (>= 1.0.0) - rake (>= 12.2) - thor (~> 1.0, >= 1.2.2) - tsort (>= 0.2) - zeitwerk (~> 2.6) - -GEM - remote: https://rubygems.org/ - specs: - action_text-trix (2.1.19) - railties - addressable (2.9.0) + addressable (2.8.9) public_suffix (>= 2.0.2, < 8.0) ast (2.4.3) base64 (0.3.0) - bcrypt (3.1.22) - bcrypt_pbkdf (1.1.2) - bigdecimal (4.1.2) + bigdecimal (4.0.1) bindex (0.8.1) - bootsnap (1.24.6) + bootsnap (1.23.0) msgpack (~> 1.2) brakeman (8.0.4) racc builder (3.3.0) - bundler-audit (0.9.3) - bundler (>= 1.2.0) - thor (~> 1.0) capybara (3.40.0) addressable matrix @@ -129,8 +95,6 @@ GEM rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) - childprocess (5.1.0) - logger (~> 1.5) concurrent-ruby (1.3.6) connection_pool (3.0.2) crass (1.0.6) @@ -138,65 +102,35 @@ GEM debug (1.11.1) irb (~> 1.10) reline (>= 0.3.8) - dotenv (3.2.0) drb (2.2.3) - ed25519 (1.4.0) - erb (6.0.4) + erb (6.0.2) + error_highlight (0.7.1) erubi (1.13.1) - et-orbi (1.4.0) - tzinfo - ffi (1.17.4-aarch64-linux-gnu) - ffi (1.17.4-aarch64-linux-musl) - ffi (1.17.4-arm-linux-gnu) - ffi (1.17.4-arm-linux-musl) - ffi (1.17.4-arm64-darwin) - ffi (1.17.4-x86_64-linux-gnu) - ffi (1.17.4-x86_64-linux-musl) - fugit (1.12.2) - et-orbi (~> 1.4) - raabro (~> 1.4) globalid (1.3.0) activesupport (>= 6.1) i18n (1.14.8) concurrent-ruby (~> 1.0) - image_processing (1.14.0) - mini_magick (>= 4.9.5, < 6) - ruby-vips (>= 2.0.17, < 3) importmap-rails (2.2.3) actionpack (>= 6.0.0) activesupport (>= 6.0.0) railties (>= 6.0.0) io-console (0.8.2) - irb (1.18.0) + irb (1.17.0) pp (>= 0.6.0) prism (>= 1.3.0) rdoc (>= 4.0.0) reline (>= 0.4.2) - jbuilder (2.15.1) + jbuilder (2.14.1) actionview (>= 7.0.0) activesupport (>= 7.0.0) - json (2.19.8) - kamal (2.11.0) - activesupport (>= 7.0) - base64 (~> 0.2) - bcrypt_pbkdf (~> 1.0) - concurrent-ruby (~> 1.2) - dotenv (~> 3.1) - ed25519 (~> 1.4) - net-ssh (~> 7.3) - sshkit (>= 1.23.0, < 2.0) - thor (~> 1.3) - zeitwerk (>= 2.6.18, < 3.0) - language_server-protocol (3.17.0.5) - launchy (3.1.1) + json (2.18.1) + json-schema (6.1.0) addressable (~> 2.8) - childprocess (~> 5.0) - logger (~> 1.6) - letter_opener (1.10.0) - launchy (>= 2.2, < 4) + bigdecimal (>= 3.1, < 5) + language_server-protocol (3.17.0.5) lint_roller (1.1.0) logger (1.7.0) - loofah (2.25.1) + loofah (2.25.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) mail (2.9.0) @@ -205,73 +139,85 @@ GEM net-imap net-pop net-smtp - marcel (1.2.1) + marcel (1.1.0) matrix (0.4.3) - mini_magick (5.3.1) - logger + mcp (0.7.1) + json-schema (>= 4.1) mini_mime (1.1.5) - minitest (6.0.6) + minitest (6.0.2) drb (~> 2.0) prism (~> 1.5) - msgpack (1.8.2) - net-imap (0.6.4.1) + msgpack (1.8.0) + net-imap (0.6.3) date net-protocol net-pop (0.1.2) net-protocol net-protocol (0.2.2) timeout - net-scp (4.1.0) - net-ssh (>= 2.6.5, < 8.0.0) - net-sftp (4.0.0) - net-ssh (>= 5.0.0, < 8.0.0) net-smtp (0.5.1) net-protocol - net-ssh (7.3.2) nio4r (2.7.5) - nokogiri (1.19.3-aarch64-linux-gnu) + nokogiri (1.19.1-aarch64-linux-gnu) + racc (~> 1.4) + nokogiri (1.19.1-aarch64-linux-musl) racc (~> 1.4) - nokogiri (1.19.3-aarch64-linux-musl) + nokogiri (1.19.1-arm-linux-gnu) racc (~> 1.4) - nokogiri (1.19.3-arm-linux-gnu) + nokogiri (1.19.1-arm-linux-musl) racc (~> 1.4) - nokogiri (1.19.3-arm-linux-musl) + nokogiri (1.19.1-arm64-darwin) racc (~> 1.4) - nokogiri (1.19.3-arm64-darwin) + nokogiri (1.19.1-x86_64-darwin) racc (~> 1.4) - nokogiri (1.19.3-x86_64-linux-gnu) + nokogiri (1.19.1-x86_64-linux-gnu) racc (~> 1.4) - nokogiri (1.19.3-x86_64-linux-musl) + nokogiri (1.19.1-x86_64-linux-musl) racc (~> 1.4) - ostruct (0.6.3) - parallel (2.1.0) - parser (3.3.11.1) + parallel (1.27.0) + parser (3.3.10.2) ast (~> 2.4.1) racc + pg (1.6.3) + pg (1.6.3-aarch64-linux) + pg (1.6.3-aarch64-linux-musl) + pg (1.6.3-arm64-darwin) + pg (1.6.3-x86_64-darwin) + pg (1.6.3-x86_64-linux) + pg (1.6.3-x86_64-linux-musl) pp (0.6.3) prettyprint prettyprint (0.2.0) prism (1.9.0) - propshaft (1.3.2) - actionpack (>= 7.0.0) - activesupport (>= 7.0.0) - rack - psych (5.4.0) + psych (5.3.1) date stringio - public_suffix (7.0.5) + public_suffix (7.0.2) puma (8.0.2) nio4r (~> 2.0) - raabro (1.4.0) racc (1.8.1) - rack (3.2.6) - rack-session (2.1.2) + rack (3.2.5) + rack-session (2.1.1) base64 (>= 0.1.0) rack (>= 3.0.0) rack-test (2.2.0) rack (>= 1.3) rackup (2.3.1) rack (>= 3) + rails (8.1.2) + actioncable (= 8.1.2) + actionmailbox (= 8.1.2) + actionmailer (= 8.1.2) + actionpack (= 8.1.2) + actiontext (= 8.1.2) + actionview (= 8.1.2) + activejob (= 8.1.2) + activemodel (= 8.1.2) + activerecord (= 8.1.2) + activestorage (= 8.1.2) + activesupport (= 8.1.2) + bundler (>= 1.15.0) + railties (= 8.1.2) rails-dom-testing (2.3.0) activesupport (>= 5.0.0) minitest @@ -279,35 +225,45 @@ GEM rails-html-sanitizer (1.7.0) loofah (~> 2.25) 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) + railties (8.1.2) + actionpack (= 8.1.2) + activesupport (= 8.1.2) + irb (~> 1.13) + rackup (>= 1.0.0) + rake (>= 12.2) + thor (~> 1.0, >= 1.2.2) + tsort (>= 0.2) + zeitwerk (~> 2.6) rainbow (3.1.1) - rake (13.4.2) + rake (13.3.1) rdoc (7.2.0) erb psych (>= 4.0.0) tsort - regexp_parser (2.12.0) + regexp_parser (2.11.3) reline (0.6.3) io-console (~> 0.5) rexml (3.4.4) - rubocop (1.87.0) + rubocop (1.85.0) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) - parallel (>= 1.10) + mcp (~> 0.6) + parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 2.9.3, < 3.0) rubocop-ast (>= 1.49.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.49.1) + rubocop-ast (1.49.0) parser (>= 3.3.7.2) prism (~> 1.7) rubocop-performance (1.26.1) lint_roller (~> 1.1) rubocop (>= 1.75.0, < 2.0) rubocop-ast (>= 1.47.1, < 2.0) - rubocop-rails (2.35.4) + rubocop-rails (2.34.3) activesupport (>= 4.2.0) lint_roller (~> 1.1) rack (>= 1.1) @@ -318,65 +274,27 @@ GEM rubocop-performance (>= 1.24) rubocop-rails (>= 2.30) ruby-progressbar (1.13.0) - ruby-vips (2.3.0) - ffi (~> 1.12) - logger - rubyzip (3.3.1) + rubyzip (3.2.2) securerandom (0.4.1) - selenium-webdriver (4.44.0) + selenium-webdriver (4.41.0) base64 (~> 0.2) logger (~> 1.4) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 4.0) websocket (~> 1.0) - solid_cable (4.0.0) - actioncable (>= 7.2) - activejob (>= 7.2) - activerecord (>= 7.2) - railties (>= 7.2) - solid_cache (1.0.10) - activejob (>= 7.2) - activerecord (>= 7.2) - railties (>= 7.2) - solid_queue (1.4.0) - activejob (>= 7.1) - activerecord (>= 7.1) - concurrent-ruby (>= 1.3.1) - fugit (~> 1.11) - railties (>= 7.1) - thor (>= 1.3.1) - sqlite3 (2.9.5-aarch64-linux-gnu) - sqlite3 (2.9.5-aarch64-linux-musl) - sqlite3 (2.9.5-arm-linux-gnu) - sqlite3 (2.9.5-arm-linux-musl) - sqlite3 (2.9.5-arm64-darwin) - sqlite3 (2.9.5-x86_64-linux-gnu) - sqlite3 (2.9.5-x86_64-linux-musl) - sshkit (1.25.0) - base64 + sprockets (4.2.2) + concurrent-ruby (~> 1.0) logger - net-scp (>= 1.1.2) - net-sftp (>= 2.1.2) - net-ssh (>= 2.8.0) - ostruct + rack (>= 2.2.4, < 4) + sprockets-rails (3.5.2) + actionpack (>= 6.1) + activesupport (>= 6.1) + sprockets (>= 3.0.0) stimulus-rails (1.3.4) railties (>= 6.0.0) stringio (3.2.0) - tailwindcss-rails (4.4.0) - railties (>= 7.0.0) - tailwindcss-ruby (~> 4.0) - tailwindcss-ruby (4.3.0) - tailwindcss-ruby (4.3.0-aarch64-linux-gnu) - tailwindcss-ruby (4.3.0-aarch64-linux-musl) - tailwindcss-ruby (4.3.0-arm64-darwin) - tailwindcss-ruby (4.3.0-x86_64-linux-gnu) - tailwindcss-ruby (4.3.0-x86_64-linux-musl) thor (1.5.0) - thruster (0.1.21) - thruster (0.1.21-aarch64-linux) - thruster (0.1.21-arm64-darwin) - thruster (0.1.21-x86_64-linux) - timeout (0.6.1) + timeout (0.6.0) tsort (0.2.0) turbo-rails (2.0.23) actionpack (>= 7.1.0) @@ -393,13 +311,13 @@ GEM bindex (>= 0.4.0) railties (>= 8.0.0) websocket (1.2.11) - websocket-driver (0.8.1) + websocket-driver (0.8.0) base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.8.2) + zeitwerk (2.7.5) PLATFORMS aarch64-linux @@ -407,184 +325,140 @@ PLATFORMS aarch64-linux-musl arm-linux-gnu arm-linux-musl - arm64-darwin-23 + arm64-darwin + x86_64-darwin x86_64-linux x86_64-linux-gnu x86_64-linux-musl DEPENDENCIES - bcrypt (~> 3.1.7) bootsnap brakeman - bundler-audit capybara debug - image_processing (~> 1.2) + error_highlight (>= 0.4.0) importmap-rails jbuilder - kamal - letter_opener - propshaft + pg (~> 1.1) puma (>= 5.0) - rails! + rails (~> 8.0) rubocop-rails-omakase selenium-webdriver - solid_cable - solid_cache - solid_queue - sqlite3 (>= 2.1) + sprockets-rails stimulus-rails - tailwindcss-rails - thruster turbo-rails tzinfo-data web-console CHECKSUMS - action_text-trix (2.1.19) sha256=7012f59421009cf284aa651294896414d653a61a2417c9b8714c8476d2f74009 - actioncable (8.1.3) - actionmailbox (8.1.3) - actionmailer (8.1.3) - actionpack (8.1.3) - actiontext (8.1.3) - actionview (8.1.3) - activejob (8.1.3) - activemodel (8.1.3) - activerecord (8.1.3) - activestorage (8.1.3) - activesupport (8.1.3) - addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af + action_text-trix (2.1.16) sha256=f645a2c21821b8449fd1d6770708f4031c91a2eedf9ef476e9be93c64e703a8a + actioncable (8.1.2) sha256=dc31efc34cca9cdefc5c691ddb8b4b214c0ea5cd1372108cbc1377767fb91969 + actionmailbox (8.1.2) sha256=058b2fb1980e5d5a894f675475fcfa45c62631103d5a2596d9610ec81581889b + actionmailer (8.1.2) sha256=f4c1d2060f653bfe908aa7fdc5a61c0e5279670de992146582f2e36f8b9175e9 + actionpack (8.1.2) sha256=ced74147a1f0daafaa4bab7f677513fd4d3add574c7839958f7b4f1de44f8423 + actiontext (8.1.2) sha256=0bf57da22a9c19d970779c3ce24a56be31b51c7640f2763ec64aa72e358d2d2d + actionview (8.1.2) sha256=80455b2588911c9b72cec22d240edacb7c150e800ef2234821269b2b2c3e2e5b + activejob (8.1.2) sha256=908dab3713b101859536375819f4156b07bdf4c232cc645e7538adb9e302f825 + activemodel (8.1.2) sha256=e21358c11ce68aed3f9838b7e464977bc007b4446c6e4059781e1d5c03bcf33e + activerecord (8.1.2) sha256=acfbe0cadfcc50fa208011fe6f4eb01cae682ebae0ef57145ba45380c74bcc44 + activestorage (8.1.2) sha256=8a63a48c3999caeee26a59441f813f94681fc35cc41aba7ce1f836add04fba76 + activesupport (8.1.2) sha256=88842578ccd0d40f658289b0e8c842acfe9af751afee2e0744a7873f50b6fdae + addressable (2.8.9) sha256=cc154fcbe689711808a43601dee7b980238ce54368d23e127421753e46895485 ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383 base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b - bcrypt (3.1.22) sha256=1f0072e88c2d705d94aff7f2c5cb02eb3f1ec4b8368671e19112527489f29032 - bcrypt_pbkdf (1.1.2) sha256=c2414c23ce66869b3eb9f643d6a3374d8322dfb5078125c82792304c10b94cf6 - bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd + bigdecimal (4.0.1) sha256=8b07d3d065a9f921c80ceaea7c9d4ae596697295b584c296fe599dd0ad01c4a7 bindex (0.8.1) sha256=7b1ecc9dc539ed8bccfc8cb4d2732046227b09d6f37582ff12e50a5047ceb17e - bootsnap (1.24.6) sha256=c60bab88c70332290f0a2636a288f675299eb4f804a02a3c085b42eca9da164a + bootsnap (1.23.0) sha256=c1254f458d58558b58be0f8eb8f6eec2821456785b7cdd1e16248e2020d3f214 brakeman (8.0.4) sha256=7bf921fa9638544835df9aa7b3e720a9a72c0267f34f92135955edd80d4dcf6f builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f bundler (4.0.12) sha256=7f8b757d28dfb636e7b24fba2344ac6dd13b5b24f4b46d62573d483f211825ac - bundler-audit (0.9.3) sha256=81c8766c71e47d0d28a0f98c7eed028539f21a6ea3cd8f685eb6f42333c9b4e9 capybara (3.40.0) sha256=42dba720578ea1ca65fd7a41d163dd368502c191804558f6e0f71b391054aeef - childprocess (5.1.0) sha256=9a8d484be2fd4096a0e90a0cd3e449a05bc3aa33f8ac9e4d6dcef6ac1455b6ec concurrent-ruby (1.3.6) sha256=6b56837e1e7e5292f9864f34b69c5a2cbc75c0cf5338f1ce9903d10fa762d5ab connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a crass (1.0.6) sha256=dc516022a56e7b3b156099abc81b6d2b08ea1ed12676ac7a5657617f012bd45d date (3.5.1) sha256=750d06384d7b9c15d562c76291407d89e368dda4d4fff957eb94962d325a0dc0 debug (1.11.1) sha256=2e0b0ac6119f2207a6f8ac7d4a73ca8eb4e440f64da0a3136c30343146e952b6 - dotenv (3.2.0) sha256=e375b83121ea7ca4ce20f214740076129ab8514cd81378161f11c03853fe619d drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373 - ed25519 (1.4.0) sha256=16e97f5198689a154247169f3453ef4cfd3f7a47481fde0ae33206cdfdcac506 - erb (6.0.4) sha256=38e3803694be357fe2bfe312487c74beaf9fb4e5beb3e22498952fe1645b95d9 + erb (6.0.2) sha256=9fe6264d44f79422c87490a1558479bd0e7dad4dd0e317656e67ea3077b5242b + error_highlight (0.7.1) sha256=1fcdb7330e56982c33e85f57000fec9c4397ece93bf59b73a51e517dff693781 erubi (1.13.1) sha256=a082103b0885dbc5ecf1172fede897f9ebdb745a4b97a5e8dc63953db1ee4ad9 - et-orbi (1.4.0) sha256=6c7e3c90779821f9e3b324c5e96fda9767f72995d6ae435b96678a4f3e2de8bc - ffi (1.17.4-aarch64-linux-gnu) sha256=b208f06f91ffd8f5e1193da3cae3d2ccfc27fc36fba577baf698d26d91c080df - ffi (1.17.4-aarch64-linux-musl) sha256=9286b7a615f2676245283aef0a0a3b475ae3aae2bb5448baace630bb77b91f39 - ffi (1.17.4-arm-linux-gnu) sha256=d6dbddf7cb77bf955411af5f187a65b8cd378cb003c15c05697f5feee1cb1564 - ffi (1.17.4-arm-linux-musl) sha256=9d4838ded0465bef6e2426935f6bcc93134b6616785a84ffd2a3d82bc3cf6f95 - ffi (1.17.4-arm64-darwin) sha256=19071aaf1419251b0a46852abf960e77330a3b334d13a4ab51d58b31a937001b - ffi (1.17.4-x86_64-linux-gnu) sha256=9d3db14c2eae074b382fa9c083fe95aec6e0a1451da249eab096c34002bc752d - ffi (1.17.4-x86_64-linux-musl) sha256=3fdf9888483de005f8ef8d1cf2d3b20d86626af206cbf780f6a6a12439a9c49e - fugit (1.12.2) sha256=643f2bf28db263bd400cbf8e0dd8b76b2c9b94bdb130e12d2394de04d9c20e5e globalid (1.3.0) sha256=05c639ad6eb4594522a0b07983022f04aa7254626ab69445a0e493aa3786ff11 i18n (1.14.8) sha256=285778639134865c5e0f6269e0b818256017e8cde89993fdfcbfb64d088824a5 - image_processing (1.14.0) sha256=754cc169c9c262980889bec6bfd325ed1dafad34f85242b5a07b60af004742fb importmap-rails (2.2.3) sha256=7101be2a4dc97cf1558fb8f573a718404c5f6bcfe94f304bf1f39e444feeb16a io-console (0.8.2) sha256=d6e3ae7a7cc7574f4b8893b4fca2162e57a825b223a177b7afa236c5ef9814cc - irb (1.18.0) sha256=de9454a0703a54704b9811a5ef31a60c86949fbf4013fcf244fabc7c775248e3 - jbuilder (2.15.1) sha256=2430bec28fb0cebacb5875b1009cf9d8bc3c303ccb810c4c8b062a4b51457637 - json (2.19.8) sha256=6354310fd76ef69b87d5bd1f38b40d730613baf90b6803d2d0a48f618d32dfaa - kamal (2.11.0) sha256=1408864425e0dec7e0a14d712a3b13f614e9f3a425b7661d3f9d287a51d7dd75 + irb (1.17.0) sha256=168c4ddb93d8a361a045c41d92b2952c7a118fa73f23fe14e55609eb7a863aae + jbuilder (2.14.1) sha256=4eb26376ff60ef100cb4fd6fd7533cd271f9998327e86adf20fd8c0e69fabb42 + json (2.18.1) sha256=fe112755501b8d0466b5ada6cf50c8c3f41e897fa128ac5d263ec09eedc9f986 + json-schema (6.1.0) sha256=6bf70a2cfb6dfd5a06da28093fa8190f324c88eabd36a7f47097f227321dc702 language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc - launchy (3.1.1) sha256=72b847b5cc961589dde2c395af0108c86ff0119f42d4648d25b5440ebb10059e - letter_opener (1.10.0) sha256=2ff33f2e3b5c3c26d1959be54b395c086ca6d44826e8bf41a14ff96fdf1bdbb2 lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87 logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203 - loofah (2.25.1) sha256=d436c73dbd0c1147b16c4a41db097942d217303e1f7728704b37e4df9f6d2e04 + loofah (2.25.0) sha256=df5ed7ac3bac6a4ec802df3877ee5cc86d027299f8952e6243b3dac446b060e6 mail (2.9.0) sha256=6fa6673ecd71c60c2d996260f9ee3dd387d4673b8169b502134659ece6d34941 - marcel (1.2.1) sha256=1678e9360e32f9eafa917c80029e2f6d10b2715c66a4b87b6d0da9b9cd1f859f + marcel (1.1.0) sha256=fdcfcfa33cc52e93c4308d40e4090a5d4ea279e160a7f6af988260fa970e0bee matrix (0.4.3) sha256=a0d5ab7ddcc1973ff690ab361b67f359acbb16958d1dc072b8b956a286564c5b - mini_magick (5.3.1) sha256=29395dfd76badcabb6403ee5aff6f681e867074f8f28ce08d78661e9e4a351c4 + mcp (0.7.1) sha256=fa967895d6952bad0d981ea907731d8528d2c246d2079d56a9c8bae83d14f1c7 mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef - minitest (6.0.6) sha256=153ea36d1d987a62942382b61075745042a2b3123b1cd48f4c3675af9cc7d6f1 - msgpack (1.8.2) sha256=e440d11c99d6dfe8b2fbc4feb74c3518c1ba024357c70bbd734d9bb1a44d0d25 - net-imap (0.6.4.1) sha256=29f0360d75a7efd3539f16ac1957dea5c0a51ddeceb348db4553c3120914ea0d + minitest (6.0.2) sha256=db6e57956f6ecc6134683b4c87467d6dd792323c7f0eea7b93f66bd284adbc3d + msgpack (1.8.0) sha256=e64ce0212000d016809f5048b48eb3a65ffb169db22238fb4b72472fecb2d732 + net-imap (0.6.3) sha256=9bab75f876596d09ee7bf911a291da478e0cd6badc54dfb82874855ccc82f2ad net-pop (0.1.2) sha256=848b4e982013c15b2f0382792268763b748cce91c9e91e36b0f27ed26420dff3 net-protocol (0.2.2) sha256=aa73e0cba6a125369de9837b8d8ef82a61849360eba0521900e2c3713aa162a8 - net-scp (4.1.0) sha256=a99b0b92a1e5d360b0de4ffbf2dc0c91531502d3d4f56c28b0139a7c093d1a5d - net-sftp (4.0.0) sha256=65bb91c859c2f93b09826757af11b69af931a3a9155050f50d1b06d384526364 net-smtp (0.5.1) sha256=ed96a0af63c524fceb4b29b0d352195c30d82dd916a42f03c62a3a70e5b70736 - net-ssh (7.3.2) sha256=65029e213c380e20e5fd92ece663934ab0a0fe888e0cd7cc6a5b664074362dd4 nio4r (2.7.5) sha256=6c90168e48fb5f8e768419c93abb94ba2b892a1d0602cb06eef16d8b7df1dca1 - nokogiri (1.19.3-aarch64-linux-gnu) sha256=46b89e5d7b9e844c2ee360794240c6ea2a4e6fa0c5892a4ed487db621224b639 - nokogiri (1.19.3-aarch64-linux-musl) sha256=8392dfdcd21be7a94dbbe9ccc138dea01b97b24cb2dc02a114ca98bfb1d9a0b7 - nokogiri (1.19.3-arm-linux-gnu) sha256=3919d5ffc334ad778a4a9eb88fda7dcb8b1fb58c8a52ac640c6dcd2f038e774f - nokogiri (1.19.3-arm-linux-musl) sha256=9ce1cb6346bb9c67b1550eb537aa183ead91e4b6eadb2f36ade02d8dd2a79fb6 - nokogiri (1.19.3-arm64-darwin) sha256=71b9bd424b1b7abc18b05052a1a3cfd3627abdca62be280854cc411791357e42 - nokogiri (1.19.3-x86_64-linux-gnu) sha256=2f5078620fe12e83669b5b17311b32532a8153d02eee7ad06948b926d6080976 - nokogiri (1.19.3-x86_64-linux-musl) sha256=248c906d2166eca5efb56d52fdee5f9a1f51d69a72e2b64fdac647b4ce39ea3f - ostruct (0.6.3) sha256=95a2ed4a4bd1d190784e666b47b2d3f078e4a9efda2fccf18f84ddc6538ed912 - parallel (2.1.0) sha256=b35258865c2e31134c5ecb708beaaf6772adf9d5efae28e93e99260877b09356 - parser (3.3.11.1) sha256=d17ace7aabe3e72c3cc94043714be27cc6f852f104d81aa284c2281aecc65d54 + nokogiri (1.19.1-aarch64-linux-gnu) sha256=cfdb0eafd9a554a88f12ebcc688d2b9005f9fce42b00b970e3dc199587b27f32 + nokogiri (1.19.1-aarch64-linux-musl) sha256=1e2150ab43c3b373aba76cd1190af7b9e92103564063e48c474f7600923620b5 + nokogiri (1.19.1-arm-linux-gnu) sha256=0a39ed59abe3bf279fab9dd4c6db6fe8af01af0608f6e1f08b8ffa4e5d407fa3 + nokogiri (1.19.1-arm-linux-musl) sha256=3a18e559ee499b064aac6562d98daab3d39ba6cbb4074a1542781b2f556db47d + nokogiri (1.19.1-arm64-darwin) sha256=dfe2d337e6700eac47290407c289d56bcf85805d128c1b5a6434ddb79731cb9e + nokogiri (1.19.1-x86_64-darwin) sha256=7093896778cc03efb74b85f915a775862730e887f2e58d6921e3fa3d981e68bf + nokogiri (1.19.1-x86_64-linux-gnu) sha256=1a4902842a186b4f901078e692d12257678e6133858d0566152fe29cdb98456a + nokogiri (1.19.1-x86_64-linux-musl) sha256=4267f38ad4fc7e52a2e7ee28ed494e8f9d8eb4f4b3320901d55981c7b995fc23 + parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130 + parser (3.3.10.2) sha256=6f60c84aa4bdcedb6d1a2434b738fe8a8136807b6adc8f7f53b97da9bc4e9357 + pg (1.6.3) sha256=1388d0563e13d2758c1089e35e973a3249e955c659592d10e5b77c468f628a99 + pg (1.6.3-aarch64-linux) sha256=0698ad563e02383c27510b76bf7d4cd2de19cd1d16a5013f375dd473e4be72ea + pg (1.6.3-aarch64-linux-musl) sha256=06a75f4ea04b05140146f2a10550b8e0d9f006a79cdaf8b5b130cde40e3ecc2c + pg (1.6.3-arm64-darwin) sha256=7240330b572e6355d7c75a7de535edb5dfcbd6295d9c7777df4d9dddfb8c0e5f + pg (1.6.3-x86_64-darwin) sha256=ee2e04a17c0627225054ffeb43e31a95be9d7e93abda2737ea3ce4a62f2729d6 + pg (1.6.3-x86_64-linux) sha256=5d9e188c8f7a0295d162b7b88a768d8452a899977d44f3274d1946d67920ae8d + pg (1.6.3-x86_64-linux-musl) sha256=9c9c90d98c72f78eb04c0f55e9618fe55d1512128e411035fe229ff427864009 pp (0.6.3) sha256=2951d514450b93ccfeb1df7d021cae0da16e0a7f95ee1e2273719669d0ab9df6 prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193 prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85 - propshaft (1.3.2) sha256=1d56a3e56a92c21bfc29caf07406b5386b00d4c47ddf357cf989a5a234b1389e - psych (5.4.0) sha256=14f72d69a611af663d7d70e4a7b67d9eb1f3ae9f8d916b478961d5a0075ba5b7 - public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623 + psych (5.3.1) sha256=eb7a57cef10c9d70173ff74e739d843ac3b2c019a003de48447b2963d81b1974 + public_suffix (7.0.2) sha256=9114090c8e4e7135c1fd0e7acfea33afaab38101884320c65aaa0ffb8e26a857 puma (8.0.2) sha256=c8ed871dfbbe66448ea9ffd46692342d9804d4071522b52b5331b7b6e7b686fb - raabro (1.4.0) sha256=d4fa9ff5172391edb92b242eed8be802d1934b1464061ae5e70d80962c5da882 racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f - rack (3.2.6) sha256=5ed78e1f73b2e25679bec7d45ee2d4483cc4146eb1be0264fc4d94cb5ef212c2 - rack-session (2.1.2) sha256=595434f8c0c3473ae7d7ac56ecda6cc6dfd9d37c0b2b5255330aa1576967ffe8 + rack (3.2.5) sha256=4cbd0974c0b79f7a139b4812004a62e4c60b145cba76422e288ee670601ed6d3 + rack-session (2.1.1) sha256=0b6dc07dea7e4b583f58a48e8b806d4c9f1c6c9214ebc202ec94562cbea2e4e9 rack-test (2.2.0) sha256=005a36692c306ac0b4a9350355ee080fd09ddef1148a5f8b2ac636c720f5c463 rackup (2.3.1) sha256=6c79c26753778e90983761d677a48937ee3192b3ffef6bc963c0950f94688868 - rails (8.1.3) + rails (8.1.2) sha256=5069061b23dfa8706b9f0159ae8b9d35727359103178a26962b868a680ba7d95 rails-dom-testing (2.3.0) sha256=8acc7953a7b911ca44588bf08737bc16719f431a1cc3091a292bca7317925c1d rails-html-sanitizer (1.7.0) sha256=28b145cceaf9cc214a9874feaa183c3acba036c9592b19886e0e45efc62b1e89 - railties (8.1.3) + railties (8.1.2) sha256=1289ece76b4f7668fc46d07e55cc992b5b8751f2ad85548b7da351b8c59f8055 rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a - rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701 + rake (13.3.1) sha256=8c9e89d09f66a26a01264e7e3480ec0607f0c497a861ef16063604b1b08eb19c rdoc (7.2.0) sha256=8650f76cd4009c3b54955eb5d7e3a075c60a57276766ebf36f9085e8c9f23192 - regexp_parser (2.12.0) sha256=35a916a1d63190ab5c9009457136ae5f3c0c7512d60291d0d1378ba18ce08ebb + regexp_parser (2.11.3) sha256=ca13f381a173b7a93450e53459075c9b76a10433caadcb2f1180f2c741fc55a4 reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835 rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142 - rubocop (1.87.0) sha256=b9d9ddf55116a513f8ef2c7ae660662d8b49301f118d3f0df61865b33a5c188d - rubocop-ast (1.49.1) sha256=4412f3ee70f6fe4546cc489548e0f6fcf76cafcfa80fa03af67098ffed755035 + rubocop (1.85.0) sha256=317407feb681a07d54f64d2f9e1d6b6af1ce7678e51cd658e3ad8bd66da48c01 + rubocop-ast (1.49.0) sha256=49c3676d3123a0923d333e20c6c2dbaaae2d2287b475273fddee0c61da9f71fd rubocop-performance (1.26.1) sha256=cd19b936ff196df85829d264b522fd4f98b6c89ad271fa52744a8c11b8f71834 - rubocop-rails (2.35.4) sha256=3aeaa325439c89950e8327565682ea794065d08e2ecbbfe95032bfa295a35df5 + rubocop-rails (2.34.3) sha256=10d37989024865ecda8199f311f3faca990143fbac967de943f88aca11eb9ad2 rubocop-rails-omakase (1.1.0) sha256=2af73ac8ee5852de2919abbd2618af9c15c19b512c4cfc1f9a5d3b6ef009109d ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33 - ruby-vips (2.3.0) sha256=e685ec02c13969912debbd98019e50492e12989282da5f37d05f5471442f5374 - rubyzip (3.3.1) sha256=2ed92112c7c43ba2b2527f35e6d99d9c2c99270b640aad5227516436481b1e4e + rubyzip (3.2.2) sha256=c0ed99385f0625415c8f05bcae33fe649ed2952894a95ff8b08f26ca57ea5b3c securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1 - selenium-webdriver (4.44.0) sha256=6f1df072529af369589c46f0e01132952aabb250cfd683c274d74dc1eb5d8477 - solid_cable (4.0.0) sha256=8379680ef6bf36e195eb876a6306ea290f87d5fa10bc4a757bc2a918f83229b5 - solid_cache (1.0.10) sha256=bc05a2fb3ac78a6f43cbb5946679cf9db67dd30d22939ededc385cb93e120d41 - solid_queue (1.4.0) sha256=e6a18d196f0b27cb6e3c77c5b31258b05fb634f8ed64fb1866ed164047216c2a - sqlite3 (2.9.5-aarch64-linux-gnu) sha256=78075b6337d3d182c6d2b4691049ed45cd220826160c9ea18946bf6a1de200dc - sqlite3 (2.9.5-aarch64-linux-musl) sha256=18c801185deb4adc01ddb281e8f672a39e3d1729979ca91e39439cd3eac0402d - sqlite3 (2.9.5-arm-linux-gnu) sha256=1bdfca0c7d63998c60b0f4a8e3c8df2d33800ccc4abd2d612eddbbbc92a4c48b - sqlite3 (2.9.5-arm-linux-musl) sha256=bae1109d12b2e9f588455967729b008e1ff4feb7761749df695019c9079913c6 - sqlite3 (2.9.5-arm64-darwin) sha256=d0cf444a70fc9395d513cfbcc1e6719e224aa645314e3824cb0474c721425aa2 - sqlite3 (2.9.5-x86_64-linux-gnu) sha256=233dbcb6714148dd23bc5aeb33e8efd6eac974969564ddd5794c23d5f52b231e - sqlite3 (2.9.5-x86_64-linux-musl) sha256=e7d3a7474e8af0f96150c21abc203fbab5437206bfcdf11deab7741c0ca516f2 - sshkit (1.25.0) sha256=c8c6543cdb60f91f1d277306d585dd11b6a064cb44eab0972827e4311ff96744 + selenium-webdriver (4.41.0) sha256=cdc1173cd55cf186022cea83156cc2d0bec06d337e039b02ad25d94e41bedd22 + sprockets (4.2.2) sha256=761e5a49f1c288704763f73139763564c845a8f856d52fba013458f8af1b59b1 + sprockets-rails (3.5.2) sha256=a9e88e6ce9f8c912d349aa5401509165ec42326baf9e942a85de4b76dbc4119e stimulus-rails (1.3.4) sha256=765676ffa1f33af64ce026d26b48e8ffb2e0b94e0f50e9119e11d6107d67cb06 stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1 - tailwindcss-rails (4.4.0) sha256=efa2961351a52acebe616e645a81a30bb4f27fde46cc06ce7688d1cd1131e916 - tailwindcss-ruby (4.3.0) sha256=4fd3219d97419d2b4f8c0f52c8a73972e55e29a0e0da38fb5fe8a16bf1a68b51 - tailwindcss-ruby (4.3.0-aarch64-linux-gnu) sha256=e43b94aba29785bba71b5868bab29b63ae48911fee038b2b5e9e5148baca3886 - tailwindcss-ruby (4.3.0-aarch64-linux-musl) sha256=ae2aff1e8482a9ba8723b57fbfa50cbf009788587ca9763f00489f3c5639e6ef - tailwindcss-ruby (4.3.0-arm64-darwin) sha256=828f49ddb3f19f67b36a1c5cb5c25175f7c93619a2ecf66e95d74408db9b73fd - tailwindcss-ruby (4.3.0-x86_64-linux-gnu) sha256=0d1b0f491990c735d2f7bf5dbaedf33ddc635fd800b7244b39b30b25a8616cb2 - tailwindcss-ruby (4.3.0-x86_64-linux-musl) sha256=71e83a3f5f40e1cd7c28e7e6328a9c899cb2c0c98f6b42f29de968fff221ef75 thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73 - thruster (0.1.21) sha256=dc67928f36e5894844579a95e45637a5091db7a7ea05468ee8c2c6eb0a3f77cf - thruster (0.1.21-aarch64-linux) sha256=f5aff78fb7a6431ed3d6ab4bde03a89c461e9a73981dbc97d6990d85c3db235c - thruster (0.1.21-arm64-darwin) sha256=bd8db9f57fae2cbb3fe08ebab49cb47fe49608122dac23daf0ce709adfb9bfc8 - thruster (0.1.21-x86_64-linux) sha256=6e2fbcf826540a72d3710ae4db072c2333287ac2ee57e7e52f35bc10900d74a7 - timeout (0.6.1) sha256=78f57368a7e7bbadec56971f78a3f5ecbcfb59b7fcbb0a3ed6ddc08a5094accb + timeout (0.6.0) sha256=6d722ad619f96ee383a0c557ec6eb8c4ecb08af3af62098a0be5057bf00de1af tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f turbo-rails (2.0.23) sha256=ee0d90733aafff056cf51ff11e803d65e43cae258cc55f6492020ec1f9f9315f tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b @@ -594,10 +468,10 @@ CHECKSUMS useragent (0.16.11) sha256=700e6413ad4bb954bb63547fa098dddf7b0ebe75b40cc6f93b8d54255b173844 web-console (4.3.0) sha256=e13b71301cdfc2093f155b5aa3a622db80b4672d1f2f713119cc7ec7ac6a6da4 websocket (1.2.11) sha256=b7e7a74e2410b5e85c25858b26b3322f29161e300935f70a0e0d3c35e0462737 - websocket-driver (0.8.1) sha256=5ab238238ce230e5d4b262d2be39624c867914eab99171dc4952b58b577c2d96 + websocket-driver (0.8.0) sha256=ed0dba4b943c22f17f9a734817e808bc84cdce6a7e22045f5315aa57676d4962 websocket-extensions (0.1.5) sha256=1c6ba63092cda343eb53fc657110c71c754c56484aad42578495227d717a8241 xpath (3.2.0) sha256=6dfda79d91bb3b949b947ecc5919f042ef2f399b904013eb3ef6d20dd3a4082e - zeitwerk (2.8.2) sha256=7212a61311083c604184b1ea2574b9aa05cd14f855a0841c06985cabe9181d12 + zeitwerk (2.7.5) sha256=d8da92128c09ea6ec62c949011b00ed4a20242b255293dd66bf41545398f73dd BUNDLED WITH - 4.0.12 + 4.0.3 diff --git a/Procfile.dev b/Procfile.dev deleted file mode 100644 index da151fe..0000000 --- a/Procfile.dev +++ /dev/null @@ -1,2 +0,0 @@ -web: bin/rails server -css: bin/rails tailwindcss:watch diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js new file mode 100644 index 0000000..ddd546a --- /dev/null +++ b/app/assets/config/manifest.js @@ -0,0 +1,4 @@ +//= link_tree ../images +//= link_directory ../stylesheets .css +//= link_tree ../../javascript .js +//= link_tree ../../../vendor/javascript .js diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index fe93333..288b9ab 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -1,10 +1,15 @@ /* - * This is a manifest file that'll be compiled into application.css. + * This is a manifest file that'll be compiled into application.css, which will include all the files + * listed below. * - * With Propshaft, assets are served efficiently without preprocessing steps. You can still include - * application-wide styles in this file, but keep in mind that CSS precedence will follow the standard - * cascading order, meaning styles declared later in the document or manifest will override earlier ones, - * depending on specificity. + * Any CSS (and SCSS, if configured) file within this directory, lib/assets/stylesheets, or any plugin's + * vendor/assets/stylesheets directory can be referenced here using a relative path. * - * Consider organizing styles into separate files for maintainability. + * You're free to add application-wide styles to this file and they'll appear at the bottom of the + * compiled file so the styles you add here take precedence over styles defined in any other CSS + * files in this directory. Styles in this file should be added after the last require_* statement. + * It is generally better to create a new file per style scope. + * + *= require_tree . + *= require_self */ diff --git a/app/assets/tailwind/application.css b/app/assets/tailwind/application.css deleted file mode 100644 index f1d8c73..0000000 --- a/app/assets/tailwind/application.css +++ /dev/null @@ -1 +0,0 @@ -@import "tailwindcss"; diff --git a/app/channels/application_cable/channel.rb b/app/channels/application_cable/channel.rb new file mode 100644 index 0000000..d672697 --- /dev/null +++ b/app/channels/application_cable/channel.rb @@ -0,0 +1,4 @@ +module ApplicationCable + class Channel < ActionCable::Channel::Base + end +end diff --git a/app/channels/application_cable/connection.rb b/app/channels/application_cable/connection.rb index 4264c74..0ff5442 100644 --- a/app/channels/application_cable/connection.rb +++ b/app/channels/application_cable/connection.rb @@ -1,16 +1,4 @@ module ApplicationCable class Connection < ActionCable::Connection::Base - identified_by :current_user - - def connect - set_current_user || reject_unauthorized_connection - end - - private - def set_current_user - if session = Session.find_by(id: cookies.signed[:session_id]) - self.current_user = session.user - end - end end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 8afd42d..0d95db2 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,9 +1,4 @@ class ApplicationController < ActionController::Base - include Authentication - # Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has. allow_browser versions: :modern - - # Changes to the importmap will invalidate the etag for HTML responses - stale_when_importmap_changes end diff --git a/app/controllers/concerns/authentication.rb b/app/controllers/concerns/authentication.rb deleted file mode 100644 index 3538f48..0000000 --- a/app/controllers/concerns/authentication.rb +++ /dev/null @@ -1,52 +0,0 @@ -module Authentication - extend ActiveSupport::Concern - - included do - before_action :require_authentication - helper_method :authenticated? - end - - class_methods do - def allow_unauthenticated_access(**options) - skip_before_action :require_authentication, **options - end - end - - private - def authenticated? - resume_session - end - - def require_authentication - resume_session || request_authentication - end - - def resume_session - Current.session ||= find_session_by_cookie - end - - def find_session_by_cookie - Session.find_by(id: cookies.signed[:session_id]) if cookies.signed[:session_id] - end - - def request_authentication - session[:return_to_after_authenticating] = request.url - redirect_to new_session_path - end - - def after_authentication_url - session.delete(:return_to_after_authenticating) || root_url - end - - def start_new_session_for(user) - user.sessions.create!(user_agent: request.user_agent, ip_address: request.remote_ip).tap do |session| - Current.session = session - cookies.signed.permanent[:session_id] = { value: session.id, httponly: true, same_site: :lax } - end - end - - def terminate_session - Current.session.destroy - cookies.delete(:session_id) - end -end diff --git a/app/controllers/email_confirmations_controller.rb b/app/controllers/email_confirmations_controller.rb deleted file mode 100644 index 6671553..0000000 --- a/app/controllers/email_confirmations_controller.rb +++ /dev/null @@ -1,13 +0,0 @@ -class EmailConfirmationsController < ApplicationController - allow_unauthenticated_access only: :show - - def show - if user = User.find_by_token_for(:email_confirmation, params[:token]) - user.confirm! unless user.confirmed? - start_new_session_for user - redirect_to after_authentication_url, notice: "Your email address has been confirmed." - else - redirect_to new_session_path, alert: "Email confirmation link is invalid or has expired." - end - end -end diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb deleted file mode 100644 index 1624bbf..0000000 --- a/app/controllers/home_controller.rb +++ /dev/null @@ -1,6 +0,0 @@ -class HomeController < ApplicationController - allow_unauthenticated_access only: :show - - def show - end -end diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb deleted file mode 100644 index f95ec78..0000000 --- a/app/controllers/passwords_controller.rb +++ /dev/null @@ -1,35 +0,0 @@ -class PasswordsController < ApplicationController - allow_unauthenticated_access - before_action :set_user_by_token, only: %i[ edit update ] - rate_limit to: 10, within: 3.minutes, only: :create, with: -> { redirect_to new_password_path, alert: "Try again later." } - - def new - end - - def create - if user = User.find_by(email_address: params[:email_address]) - PasswordsMailer.reset(user).deliver_later - end - - redirect_to new_session_path, notice: "Password reset instructions sent (if user with that email address exists)." - end - - def edit - end - - def update - if @user.update(params.permit(:password, :password_confirmation)) - @user.sessions.destroy_all - redirect_to new_session_path, notice: "Password has been reset." - else - redirect_to edit_password_path(params[:token]), alert: "Passwords did not match." - end - end - - private - def set_user_by_token - @user = User.find_by_password_reset_token!(params[:token]) - rescue ActiveSupport::MessageVerifier::InvalidSignature - redirect_to new_password_path, alert: "Password reset link is invalid or has expired." - end -end diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb deleted file mode 100644 index 62e45bf..0000000 --- a/app/controllers/registrations_controller.rb +++ /dev/null @@ -1,31 +0,0 @@ -class RegistrationsController < ApplicationController - allow_unauthenticated_access only: %i[ new create ] - rate_limit to: 10, within: 3.minutes, only: :create, with: -> { redirect_to new_registration_path, alert: "Try again later." } - - def new - @user = User.new - end - - def create - # Honeypot: real users never fill this hidden field, bots do. Silently - # pretend success so spammers can't tell their submission was rejected. - if params[:nickname].present? - redirect_to new_session_path, notice: "Check your email to confirm your account before signing in." - return - end - - @user = User.new(registration_params) - - if @user.save - RegistrationMailer.confirmation(@user).deliver_later - redirect_to new_session_path, notice: "Check your email to confirm your account before signing in." - else - render :new, status: :unprocessable_entity - end - end - - private - def registration_params - params.require(:user).permit(:email_address, :password, :password_confirmation) - end -end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb deleted file mode 100644 index e3e1df6..0000000 --- a/app/controllers/sessions_controller.rb +++ /dev/null @@ -1,25 +0,0 @@ -class SessionsController < ApplicationController - allow_unauthenticated_access only: %i[ new create ] - rate_limit to: 10, within: 3.minutes, only: :create, with: -> { redirect_to new_session_path, alert: "Try again later." } - - def new - end - - def create - if user = User.authenticate_by(params.permit(:email_address, :password)) - if user.confirmed? - start_new_session_for user - redirect_to after_authentication_url - else - redirect_to new_session_path, alert: "Please confirm your email address before signing in." - end - else - redirect_to new_session_path, alert: "Try another email address or password." - end - end - - def destroy - terminate_session - redirect_to new_session_path, status: :see_other - end -end diff --git a/app/mailers/passwords_mailer.rb b/app/mailers/passwords_mailer.rb deleted file mode 100644 index 4f0ac7f..0000000 --- a/app/mailers/passwords_mailer.rb +++ /dev/null @@ -1,6 +0,0 @@ -class PasswordsMailer < ApplicationMailer - def reset(user) - @user = user - mail subject: "Reset your password", to: user.email_address - end -end diff --git a/app/mailers/registration_mailer.rb b/app/mailers/registration_mailer.rb deleted file mode 100644 index e734504..0000000 --- a/app/mailers/registration_mailer.rb +++ /dev/null @@ -1,6 +0,0 @@ -class RegistrationMailer < ApplicationMailer - def confirmation(user) - @user = user - mail subject: "Confirm your email address", to: user.email_address - end -end diff --git a/app/models/current.rb b/app/models/current.rb deleted file mode 100644 index 2bef56d..0000000 --- a/app/models/current.rb +++ /dev/null @@ -1,4 +0,0 @@ -class Current < ActiveSupport::CurrentAttributes - attribute :session - delegate :user, to: :session, allow_nil: true -end diff --git a/app/models/session.rb b/app/models/session.rb deleted file mode 100644 index cf376fb..0000000 --- a/app/models/session.rb +++ /dev/null @@ -1,3 +0,0 @@ -class Session < ApplicationRecord - belongs_to :user -end diff --git a/app/models/user.rb b/app/models/user.rb deleted file mode 100644 index e9f9d99..0000000 --- a/app/models/user.rb +++ /dev/null @@ -1,21 +0,0 @@ -class User < ApplicationRecord - has_secure_password - has_many :sessions, dependent: :destroy - - generates_token_for :email_confirmation, expires_in: 1.day do - confirmed_at - end - - normalizes :email_address, with: ->(e) { e.strip.downcase } - - validates :email_address, presence: true, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP } - validates :password, length: { minimum: 8 }, allow_nil: true - - def confirmed? - confirmed_at.present? - end - - def confirm! - update!(confirmed_at: Time.current) - end -end diff --git a/app/views/home/show.html.erb b/app/views/home/show.html.erb deleted file mode 100644 index 02e9ec0..0000000 --- a/app/views/home/show.html.erb +++ /dev/null @@ -1,20 +0,0 @@ -
- <% if authenticated? %> -

- Hello, <%= Current.user.email_address %> -

-

You're signed in.

- -
- <%= button_to "Sign out", session_path, method: :delete, class: "rounded-md px-3.5 py-2.5 bg-blue-600 hover:bg-blue-500 text-white font-medium cursor-pointer" %> -
- <% else %> -

Welcome

-

Sign in to your account or create a new one to get started.

- -
- <%= link_to "Sign in", new_session_path, class: "rounded-md px-3.5 py-2.5 bg-blue-600 hover:bg-blue-500 text-white font-medium inline-block" %> - <%= link_to "Sign up", new_registration_path, class: "rounded-md px-3.5 py-2.5 border border-gray-400 hover:bg-gray-50 text-gray-700 font-medium inline-block" %> -
- <% end %> -
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 7d51cc3..a1e212d 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -1,31 +1,23 @@ - <%= content_for(:title) || "Community Foundations" %> + <%= content_for(:title) || "Legacy Builder" %> - - <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= yield :head %> - <%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %> - <%#= tag.link rel: "manifest", href: pwa_manifest_path(format: :json) %> - + - - <%# Includes all stylesheet files in app/assets/stylesheets %> - <%= stylesheet_link_tag :app, "data-turbo-track": "reload" %> + <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> <%= javascript_importmap_tags %> -
- <%= yield %> -
+ <%= yield %> diff --git a/app/views/passwords/edit.html.erb b/app/views/passwords/edit.html.erb deleted file mode 100644 index 65798f8..0000000 --- a/app/views/passwords/edit.html.erb +++ /dev/null @@ -1,21 +0,0 @@ -
- <% if alert = flash[:alert] %> -

<%= alert %>

- <% end %> - -

Update your password

- - <%= form_with url: password_path(params[:token]), method: :put, class: "contents" do |form| %> -
- <%= form.password_field :password, required: true, autocomplete: "new-password", placeholder: "Enter new password", maxlength: 72, class: "block shadow-sm rounded-md border border-gray-400 focus:outline-solid focus:outline-blue-600 px-3 py-2 mt-2 w-full" %> -
- -
- <%= form.password_field :password_confirmation, required: true, autocomplete: "new-password", placeholder: "Repeat new password", maxlength: 72, class: "block shadow-sm rounded-md border border-gray-400 focus:outline-solid focus:outline-blue-600 px-3 py-2 mt-2 w-full" %> -
- -
- <%= form.submit "Save", class: "w-full sm:w-auto text-center rounded-md px-3.5 py-2.5 bg-blue-600 hover:bg-blue-500 text-white inline-block font-medium cursor-pointer" %> -
- <% end %> -
diff --git a/app/views/passwords/new.html.erb b/app/views/passwords/new.html.erb deleted file mode 100644 index 8360e02..0000000 --- a/app/views/passwords/new.html.erb +++ /dev/null @@ -1,17 +0,0 @@ -
- <% if alert = flash[:alert] %> -

<%= alert %>

- <% end %> - -

Forgot your password?

- - <%= form_with url: passwords_path, class: "contents" do |form| %> -
- <%= form.email_field :email_address, required: true, autofocus: true, autocomplete: "username", placeholder: "Enter your email address", value: params[:email_address], class: "block shadow-sm rounded-md border border-gray-400 focus:outline-solid focus:outline-blue-600 px-3 py-2 mt-2 w-full" %> -
- -
- <%= form.submit "Email reset instructions", class: "w-full sm:w-auto text-center rounded-lg px-3.5 py-2.5 bg-blue-600 hover:bg-blue-500 text-white inline-block font-medium cursor-pointer" %> -
- <% end %> -
diff --git a/app/views/passwords_mailer/reset.html.erb b/app/views/passwords_mailer/reset.html.erb deleted file mode 100644 index 1b09154..0000000 --- a/app/views/passwords_mailer/reset.html.erb +++ /dev/null @@ -1,6 +0,0 @@ -

- You can reset your password on - <%= link_to "this password reset page", edit_password_url(@user.password_reset_token) %>. - - This link will expire in <%= distance_of_time_in_words(0, @user.password_reset_token_expires_in) %>. -

diff --git a/app/views/passwords_mailer/reset.text.erb b/app/views/passwords_mailer/reset.text.erb deleted file mode 100644 index aecee82..0000000 --- a/app/views/passwords_mailer/reset.text.erb +++ /dev/null @@ -1,4 +0,0 @@ -You can reset your password on -<%= edit_password_url(@user.password_reset_token) %> - -This link will expire in <%= distance_of_time_in_words(0, @user.password_reset_token_expires_in) %>. diff --git a/app/views/pwa/manifest.json.erb b/app/views/pwa/manifest.json.erb index e9e263a..97dc801 100644 --- a/app/views/pwa/manifest.json.erb +++ b/app/views/pwa/manifest.json.erb @@ -1,5 +1,5 @@ { - "name": "CommunityFoundations", + "name": "LegacyBuilder", "icons": [ { "src": "/icon.png", @@ -16,7 +16,7 @@ "start_url": "/", "display": "standalone", "scope": "/", - "description": "CommunityFoundations.", + "description": "LegacyBuilder.", "theme_color": "red", "background_color": "red" } diff --git a/app/views/registration_mailer/confirmation.html.erb b/app/views/registration_mailer/confirmation.html.erb deleted file mode 100644 index 4d53bdc..0000000 --- a/app/views/registration_mailer/confirmation.html.erb +++ /dev/null @@ -1,6 +0,0 @@ -

- Welcome! Please confirm your email address by visiting - <%= link_to "this confirmation page", email_confirmation_url(token: @user.generate_token_for(:email_confirmation)) %>. - - This link will expire in 24 hours. -

diff --git a/app/views/registration_mailer/confirmation.text.erb b/app/views/registration_mailer/confirmation.text.erb deleted file mode 100644 index fe22264..0000000 --- a/app/views/registration_mailer/confirmation.text.erb +++ /dev/null @@ -1,4 +0,0 @@ -Welcome! Please confirm your email address by visiting -<%= email_confirmation_url(token: @user.generate_token_for(:email_confirmation)) %> - -This link will expire in 24 hours. diff --git a/app/views/registrations/new.html.erb b/app/views/registrations/new.html.erb deleted file mode 100644 index 14cf8d5..0000000 --- a/app/views/registrations/new.html.erb +++ /dev/null @@ -1,47 +0,0 @@ -
- <% if alert = flash[:alert] %> -

<%= alert %>

- <% end %> - -

Sign up

- - <%= form_with model: @user, url: registration_url, class: "contents" do |form| %> - <% if @user.errors.any? %> -
-
    - <% @user.errors.full_messages.each do |message| %> -
  • <%= message %>
  • - <% end %> -
-
- <% end %> - - <%# Honeypot: hidden from real users, but bots tend to fill every field. Submissions with this set are silently dropped. %> - - -
- <%= form.email_field :email_address, required: true, autofocus: true, autocomplete: "username", placeholder: "Enter your email address", class: "block shadow-sm rounded-md border border-gray-400 focus:outline-blue-600 px-3 py-2 mt-2 w-full" %> -
- -
- <%= form.password_field :password, required: true, autocomplete: "new-password", minlength: 8, placeholder: "Enter your password (min. 8 characters)", maxlength: 72, class: "block shadow-sm rounded-md border border-gray-400 focus:outline-blue-600 px-3 py-2 mt-2 w-full" %> -
- -
- <%= form.password_field :password_confirmation, required: true, autocomplete: "new-password", placeholder: "Confirm your password", maxlength: 72, class: "block shadow-sm rounded-md border border-gray-400 focus:outline-blue-600 px-3 py-2 mt-2 w-full" %> -
- -
-
- <%= form.submit "Sign up", class: "w-full sm:w-auto text-center rounded-md px-3.5 py-2.5 bg-blue-600 hover:bg-blue-500 text-white inline-block font-medium cursor-pointer" %> -
- -
- <%= link_to "Already have an account? Sign in", new_session_path, class: "text-gray-700 underline hover:no-underline" %> -
-
- <% end %> -
diff --git a/app/views/sessions/new.html.erb b/app/views/sessions/new.html.erb deleted file mode 100644 index 02c4aba..0000000 --- a/app/views/sessions/new.html.erb +++ /dev/null @@ -1,33 +0,0 @@ -
- <% if alert = flash[:alert] %> -

<%= alert %>

- <% end %> - - <% if notice = flash[:notice] %> -

<%= notice %>

- <% end %> - -

Sign in

- - <%= form_with url: session_url, class: "contents" do |form| %> -
- <%= form.email_field :email_address, required: true, autofocus: true, autocomplete: "username", placeholder: "Enter your email address", value: params[:email_address], class: "block shadow-sm rounded-md border border-gray-400 focus:outline-blue-600 px-3 py-2 mt-2 w-full" %> -
- -
- <%= form.password_field :password, required: true, autocomplete: "current-password", placeholder: "Enter your password", maxlength: 72, class: "block shadow-sm rounded-md border border-gray-400 focus:outline-blue-600 px-3 py-2 mt-2 w-full" %> -
- -
-
- <%= form.submit "Sign in", class: "w-full sm:w-auto text-center rounded-md px-3.5 py-2.5 bg-blue-600 hover:bg-blue-500 text-white inline-block font-medium cursor-pointer" %> -
- -
- <%= link_to "Sign up", new_registration_path, class: "text-gray-700 underline hover:no-underline" %> - · - <%= link_to "Forgot password?", new_password_path, class: "text-gray-700 underline hover:no-underline" %> -
-
- <% end %> -
diff --git a/bin/bundle b/bin/bundle new file mode 100755 index 0000000..5b593cb --- /dev/null +++ b/bin/bundle @@ -0,0 +1,114 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'bundle' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require "rubygems" + +m = Module.new do + module_function + + def invoked_as_script? + File.expand_path($0) == File.expand_path(__FILE__) + end + + def env_var_version + ENV["BUNDLER_VERSION"] + end + + def cli_arg_version + return unless invoked_as_script? # don't want to hijack other binstubs + return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update` + bundler_version = nil + update_index = nil + ARGV.each_with_index do |a, i| + if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN + bundler_version = a + end + next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/ + bundler_version = $1 + update_index = i + end + bundler_version + end + + def gemfile + gemfile = ENV["BUNDLE_GEMFILE"] + return gemfile if gemfile && !gemfile.empty? + + File.expand_path("../../Gemfile", __FILE__) + end + + def lockfile + lockfile = + case File.basename(gemfile) + when "gems.rb" then gemfile.sub(/\.rb$/, gemfile) + else "#{gemfile}.lock" + end + File.expand_path(lockfile) + end + + def lockfile_version + return unless File.file?(lockfile) + lockfile_contents = File.read(lockfile) + return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/ + Regexp.last_match(1) + end + + def bundler_requirement + @bundler_requirement ||= + env_var_version || cli_arg_version || + bundler_requirement_for(lockfile_version) + end + + def bundler_requirement_for(version) + return "#{Gem::Requirement.default}.a" unless version + + bundler_gem_version = Gem::Version.new(version) + + requirement = bundler_gem_version.approximate_recommendation + + return requirement unless Gem.rubygems_version < Gem::Version.new("2.7.0") + + requirement += ".a" if bundler_gem_version.prerelease? + + requirement + end + + def load_bundler! + ENV["BUNDLE_GEMFILE"] ||= gemfile + + activate_bundler + end + + def activate_bundler + gem_error = activation_error_handling do + gem "bundler", bundler_requirement + end + return if gem_error.nil? + require_error = activation_error_handling do + require "bundler/version" + end + return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION)) + warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`" + exit 42 + end + + def activation_error_handling + yield + nil + rescue StandardError, LoadError => e + e + end +end + +m.load_bundler! + +if m.invoked_as_script? + load Gem.bin_path("bundler", "bundle") +end diff --git a/bin/bundler-audit b/bin/bundler-audit deleted file mode 100755 index e2ef226..0000000 --- a/bin/bundler-audit +++ /dev/null @@ -1,6 +0,0 @@ -#!/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 diff --git a/bin/ci b/bin/ci deleted file mode 100755 index 4137ad5..0000000 --- a/bin/ci +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env ruby -require_relative "../config/boot" -require "active_support/continuous_integration" - -CI = ActiveSupport::ContinuousIntegration -require_relative "../config/ci.rb" diff --git a/bin/dev b/bin/dev deleted file mode 100755 index ad72c7d..0000000 --- a/bin/dev +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env sh - -if ! gem list foreman -i --silent; then - echo "Installing foreman..." - gem install foreman -fi - -# Default to port 3000 if not specified -export PORT="${PORT:-3000}" - -# Let the debug gem allow remote connections, -# but avoid loading until `debugger` is called -export RUBY_DEBUG_OPEN="true" -export RUBY_DEBUG_LAZY="true" - -exec foreman start -f Procfile.dev "$@" diff --git a/bin/docker-entrypoint b/bin/docker-entrypoint index ed31659..840d093 100755 --- a/bin/docker-entrypoint +++ b/bin/docker-entrypoint @@ -1,7 +1,12 @@ #!/bin/bash -e +# Enable jemalloc for reduced memory usage and latency. +if [ -z "${LD_PRELOAD+x}" ] && [ -f /usr/lib/*/libjemalloc.so.2 ]; then + export LD_PRELOAD="$(echo /usr/lib/*/libjemalloc.so.2)" +fi + # If running the rails server then create or migrate existing database -if [ "${@: -2:1}" == "./bin/rails" ] && [ "${@: -1:1}" == "server" ]; then +if [ "${1}" == "./bin/rails" ] && [ "${2}" == "server" ]; then ./bin/rails db:prepare fi diff --git a/bin/jobs b/bin/jobs deleted file mode 100755 index dcf59f3..0000000 --- a/bin/jobs +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env ruby - -require_relative "../config/environment" -require "solid_queue/cli" - -SolidQueue::Cli.start(ARGV) diff --git a/bin/kamal b/bin/kamal deleted file mode 100755 index d9ba276..0000000 --- a/bin/kamal +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -# -# This file was generated by Bundler. -# -# The application 'kamal' is installed as part of a gem, and -# this file is here to facilitate running it. -# - -ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) - -require "rubygems" -require "bundler/setup" - -load Gem.bin_path("kamal", "kamal") diff --git a/bin/rubocop b/bin/rubocop index 5a20504..40330c0 100755 --- a/bin/rubocop +++ b/bin/rubocop @@ -2,7 +2,7 @@ require "rubygems" require "bundler/setup" -# Explicit RuboCop config increases performance slightly while avoiding config confusion. +# 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") diff --git a/bin/setup b/bin/setup index 81be011..6057965 100755 --- a/bin/setup +++ b/bin/setup @@ -2,6 +2,7 @@ require "fileutils" APP_ROOT = File.expand_path("..", __dir__) +APP_NAME = "legacy-builder" def system!(*args) system(*args, exception: true) @@ -13,6 +14,7 @@ FileUtils.chdir APP_ROOT do # Add necessary setup steps to this file. puts "== Installing dependencies ==" + system! "gem install bundler --conservative" system("bundle check") || system!("bundle install") # puts "\n== Copying sample files ==" @@ -22,14 +24,14 @@ FileUtils.chdir APP_ROOT do puts "\n== Preparing database ==" system! "bin/rails db:prepare" - system! "bin/rails db:reset" if ARGV.include?("--reset") puts "\n== Removing old logs and tempfiles ==" system! "bin/rails log:clear tmp:clear" - unless ARGV.include?("--skip-server") - puts "\n== Starting development server ==" - STDOUT.flush # flush the output before exec(2) so that it displays - exec "bin/dev" - end + puts "\n== Restarting application server ==" + system! "bin/rails restart" + + # puts "\n== Configuring puma-dev ==" + # system "ln -nfs #{APP_ROOT} ~/.puma-dev/#{APP_NAME}" + # system "curl -Is https://#{APP_NAME}.test/up | head -n 1" end diff --git a/bin/thrust b/bin/thrust deleted file mode 100755 index 36bde2d..0000000 --- a/bin/thrust +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env ruby -require "rubygems" -require "bundler/setup" - -load Gem.bin_path("thruster", "thrust") diff --git a/config/application.rb b/config/application.rb index 2ed05f1..e8febc5 100644 --- a/config/application.rb +++ b/config/application.rb @@ -6,10 +6,10 @@ # you've limited to :test, :development, or :production. Bundler.require(*Rails.groups) -module CommunityFoundations +module LegacyBuilder class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. - config.load_defaults 8.1 + config.load_defaults 8.0 # 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. diff --git a/config/bundler-audit.yml b/config/bundler-audit.yml deleted file mode 100644 index e74b3af..0000000 --- a/config/bundler-audit.yml +++ /dev/null @@ -1,5 +0,0 @@ -# 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 diff --git a/config/cable.yml b/config/cable.yml index b9adc5a..ef549e8 100644 --- a/config/cable.yml +++ b/config/cable.yml @@ -1,7 +1,3 @@ -# Async adapter only works within the same process, so for manually triggering cable updates from a console, -# and seeing results in the browser, you must do so from the web console (running inside the dev process), -# not a terminal started via bin/rails console! Add "console" to any action or any ERB template view -# to make the web console appear. development: adapter: async @@ -9,9 +5,6 @@ test: adapter: test production: - adapter: solid_cable - connects_to: - database: - writing: cable - polling_interval: 0.1.seconds - message_retention: 1.day + adapter: redis + url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> + channel_prefix: legacy_builder_production diff --git a/config/cache.yml b/config/cache.yml deleted file mode 100644 index 19d4908..0000000 --- a/config/cache.yml +++ /dev/null @@ -1,16 +0,0 @@ -default: &default - store_options: - # Cap age of oldest cache entry to fulfill retention policies - # max_age: <%= 60.days.to_i %> - max_size: <%= 256.megabytes %> - namespace: <%= Rails.env %> - -development: - <<: *default - -test: - <<: *default - -production: - database: cache - <<: *default diff --git a/config/ci.rb b/config/ci.rb deleted file mode 100644 index 1712cc1..0000000 --- a/config/ci.rb +++ /dev/null @@ -1,24 +0,0 @@ -# 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: Seeds", "env RAILS_ENV=test bin/rails db:seed:replant" - - # Optional: Run system tests - # step "Tests: System", "bin/rails test:system" - - # Optional: set a green GitHub commit status to unblock PR merge. - # Requires the `gh` CLI and `gh extension install basecamp/gh-signoff`. - # 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 diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc index 4d15e49..975026a 100644 --- a/config/credentials.yml.enc +++ b/config/credentials.yml.enc @@ -1 +1 @@ -fe5wDq/Jpu9VSpWtp202nYwhXbhoVv5Zjg2dn2JEsQrsjbqfmbQ4f4rkmg9APWE2k06OenvTAxq0zM2aUXztwoQg3J0FjfwENTPuFS6CiUEXrHLDKMA+aK3E9uk7MKCl7XC+5h/7iEUx95fEfSBatn/wT6K3kVB7z1kzOjDW5XB5hWL5fePGzi/X6te4u+QGRhjPiI6Y43IT5brJkIjHrZNbJyDrJ+BXxTpeJqOG+JGHSybui9Ep09thhV92N7E3Ur9NTwaXLLGFiKpHiu2aSDdHMVh6Y21CLsf7SPGYQXkINjF5+qMnUFOtGIB/TRaHtUOYNwvWCCnck8U047x3eDN6qUw4k3vFYNGY5Ttd4pFOV0QntyCTdbjCCut3cyoFPQqCj0R/Pbd684M6CSYu/BClkCUzPGugk+w/xWUcLwDAedEix2Mot60L2o4A2bCzZCneJq12Kd8ztS/bifAsA0w057qUsipySfpFxPLVT2Lf5WwW84Ia3q8R--+TDa8t3XGh1zcO98--OnuDvOi+CEM3/lDRPCqKLw== \ No newline at end of file +KvVZZWCUG7SgKEQgyQw5IhCOQfLyU+QL2hEfPSPFOC177pImwuhVwxT/PzBrRy5q0C0C3LV7eZH8SuZkGiG/Ai8GmSZqcdp/uGj/jGVctNm+0CC9wROx4NBUdERoVkNdG6sa0WTRIMihEXOPyW2AiN2ST1mFCOZYPaDpyKNkg86gilNGP9eMnHyjtPeFQOlfc+XWb3PdrgAiIlnGBTw075g5P14Gn1eCipT6ilNdoFWdX2lqAkH8vITXk33Aif4y5SfuimYpsyksjDxOc3j7/ZeU1PyyvjsrhXtevRACzrT6HPHJSAiOf6TlrFXT7cCUnxG/onbPjND9L7s8gEV+5b7Io01E60oVQqUuhK6A1Y+/ZOwNW3Yl54lqOF2LV/jInkWbfl6NPgo18Sg7J1ZFNf+XubxU--7Gq5kzSrgITMLAlb--K5KQF0IfB0ONzswyYwXFpg== \ No newline at end of file diff --git a/config/database.yml b/config/database.yml index 302d638..acd7e39 100644 --- a/config/database.yml +++ b/config/database.yml @@ -1,40 +1,85 @@ -# SQLite. Versions 3.8.0 and up are supported. -# gem install sqlite3 +# PostgreSQL. Versions 9.3 and up are supported. # -# Ensure the SQLite 3 gem is defined in your Gemfile -# gem "sqlite3" +# Install the pg driver: +# gem install pg +# On macOS with Homebrew: +# gem install pg -- --with-pg-config=/usr/local/bin/pg_config +# On Windows: +# gem install pg +# Choose the win32 build. +# Install PostgreSQL and put its /bin directory on your path. +# +# Configure Using Gemfile +# gem "pg" # default: &default - adapter: sqlite3 - max_connections: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> - timeout: 5000 + adapter: postgresql + encoding: unicode + # For details on connection pooling, see Rails configuration guide + # https://guides.rubyonrails.org/configuring.html#database-pooling + pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + development: <<: *default - database: storage/development.sqlite3 + database: legacy_builder_development + + # The specified database role being used to connect to PostgreSQL. + # To create additional roles in PostgreSQL see `$ createuser --help`. + # When left blank, PostgreSQL will use the default role. This is + # the same name as the operating system user running Rails. + #username: legacy_builder + + # The password associated with the PostgreSQL role (username). + #password: + + # Connect on a TCP socket. Omitted by default since the client uses a + # domain socket that doesn't need configuration. Windows does not have + # domain sockets, so uncomment these lines. + #host: localhost + + # The TCP port the server listens on. Defaults to 5432. + # If your server runs on a different port number, change accordingly. + #port: 5432 + + # Schema search path. The server defaults to $user,public + #schema_search_path: myapp,sharedapp,public + + # Minimum log levels, in increasing order: + # debug5, debug4, debug3, debug2, debug1, + # log, notice, warning, error, fatal, and panic + # Defaults to warning. + #min_messages: notice # Warning: The database defined as "test" will be erased and # re-generated from your development database when you run "rake". # Do not set this db to the same as development or production. test: <<: *default - database: storage/test.sqlite3 + database: legacy_builder_test -# Store production database in the storage/ directory, which by default -# is mounted as a persistent Docker volume in config/deploy.yml. +# As with config/credentials.yml, you never want to store sensitive information, +# like your database password, in your source code. If your source code is +# ever seen by anyone, they now have access to your database. +# +# Instead, provide the password or a full connection URL as an environment +# variable when you boot the app. For example: +# +# DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase" +# +# If the connection URL is provided in the special DATABASE_URL environment +# variable, Rails will automatically merge its configuration values on top of +# the values provided in this file. Alternatively, you can specify a connection +# URL environment variable explicitly: +# +# production: +# url: <%= ENV["MY_APP_DATABASE_URL"] %> +# +# Read https://guides.rubyonrails.org/configuring.html#configuring-a-database +# for a full overview on how database connection configuration can be specified. +# production: - primary: - <<: *default - database: storage/production.sqlite3 - cache: - <<: *default - database: storage/production_cache.sqlite3 - migrations_paths: db/cache_migrate - queue: - <<: *default - database: storage/production_queue.sqlite3 - migrations_paths: db/queue_migrate - cable: - <<: *default - database: storage/production_cable.sqlite3 - migrations_paths: db/cable_migrate + <<: *default + database: legacy_builder_production + username: legacy_builder + password: <%= ENV["LEGACY_BUILDER_DATABASE_PASSWORD"] %> diff --git a/config/deploy.yml b/config/deploy.yml deleted file mode 100644 index 6cdfa89..0000000 --- a/config/deploy.yml +++ /dev/null @@ -1,119 +0,0 @@ -# Name of your application. Used to uniquely configure containers. -service: community_foundations - -# Name of the container image (use your-user/app-name on external registries). -image: community_foundations - -# Deploy to these servers. -servers: - web: - - 192.168.0.1 - # job: - # hosts: - # - 192.168.0.1 - # cmd: bin/jobs - -# Enable SSL auto certification via Let's Encrypt and allow for multiple apps on a single web server. -# If used with Cloudflare, set encryption mode in SSL/TLS setting to "Full" to enable CF-to-app encryption. -# -# Using an SSL proxy like this requires turning on config.assume_ssl and config.force_ssl in production.rb! -# -# Don't use this when deploying to multiple web servers (then you have to terminate SSL at your load balancer). -# -# proxy: -# ssl: true -# host: app.example.com - -# Where you keep your container images. -registry: - # Alternatives: hub.docker.com / registry.digitalocean.com / ghcr.io / ... - server: localhost:5555 - - # Needed for authenticated registries. - # username: your-user - - # Always use an access token rather than real password when possible. - # password: - # - KAMAL_REGISTRY_PASSWORD - -# Inject ENV variables into containers (secrets come from .kamal/secrets). -env: - secret: - - RAILS_MASTER_KEY - clear: - # Run the Solid Queue Supervisor inside the web server's Puma process to do jobs. - # When you start using multiple servers, you should split out job processing to a dedicated machine. - SOLID_QUEUE_IN_PUMA: true - - # Set number of processes dedicated to Solid Queue (default: 1) - # JOB_CONCURRENCY: 3 - - # Set number of cores available to the application on each server (default: 1). - # WEB_CONCURRENCY: 2 - - # Match this to any external database server to configure Active Record correctly - # Use community_foundations-db for a db accessory server on same machine via local kamal docker network. - # DB_HOST: 192.168.0.2 - - # Log everything from Rails - # RAILS_LOG_LEVEL: debug - -# Aliases are triggered with "bin/kamal ". You can overwrite arguments on invocation: -# "bin/kamal logs -r job" will tail logs from the first server in the job section. -aliases: - console: app exec --interactive --reuse "bin/rails console" - shell: app exec --interactive --reuse "bash" - logs: app logs -f - dbc: app exec --interactive --reuse "bin/rails dbconsole --include-password" - -# Use a persistent storage volume for sqlite database files and local Active Storage files. -# Recommended to change this to a mounted volume path that is backed up off server. -volumes: - - "community_foundations_storage:/rails/storage" - -# Bridge fingerprinted assets, like JS and CSS, between versions to avoid -# hitting 404 on in-flight requests. Combines all files from new and old -# version inside the asset_path. -asset_path: /rails/public/assets - -# Configure the image builder. -builder: - arch: amd64 - - # # Build image via remote server (useful for faster amd64 builds on arm64 computers) - # remote: ssh://docker@docker-builder-server - # - # # Pass arguments and secrets to the Docker build process - # args: - # RUBY_VERSION: ruby-4.0.2 - # secrets: - # - GITHUB_TOKEN - # - RAILS_MASTER_KEY - -# Use a different ssh user than root -# ssh: -# user: app - -# Use accessory services (secrets come from .kamal/secrets). -# accessories: -# db: -# image: mysql:8.0 -# host: 192.168.0.2 -# # Change to 3306 to expose port to the world instead of just local network. -# port: "127.0.0.1:3306:3306" -# env: -# clear: -# MYSQL_ROOT_HOST: '%' -# secret: -# - MYSQL_ROOT_PASSWORD -# files: -# - config/mysql/production.cnf:/etc/mysql/my.cnf -# - db/production.sql:/docker-entrypoint-initdb.d/setup.sql -# directories: -# - data:/var/lib/mysql -# redis: -# image: valkey/valkey:8 -# host: 192.168.0.2 -# port: 6379 -# directories: -# - data:/data diff --git a/config/environments/development.rb b/config/environments/development.rb index 5a10d1e..9b67360 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -3,7 +3,9 @@ Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. - # Make code changes take effect immediately without server restart. + # In the development environment your application's code is reloaded any time + # it changes. This slows down response time but is perfect for development + # since you don't have to restart the web server when you make code changes. config.enable_reloading = true # Do not eager load code on boot. @@ -15,18 +17,19 @@ # Enable server timing. config.server_timing = true - # Enable/disable Action Controller caching. By default Action Controller caching is disabled. - # Run rails dev:cache to toggle Action Controller caching. + # Enable/disable caching. By default caching is disabled. + # Run rails dev:cache to toggle caching. if Rails.root.join("tmp/caching-dev.txt").exist? config.action_controller.perform_caching = true config.action_controller.enable_fragment_cache_logging = true - config.public_file_server.headers = { "cache-control" => "public, max-age=#{2.days.to_i}" } + + config.cache_store = :memory_store + config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{2.days.to_i}" } else config.action_controller.perform_caching = false - end - # Change to :null_store to avoid any caching. - config.cache_store = :memory_store + config.cache_store = :null_store + end # Store uploaded files on the local file system (see config/storage.yml for options). config.active_storage.service = :local @@ -34,36 +37,30 @@ # Don't care if the mailer can't send. config.action_mailer.raise_delivery_errors = false - # Open sent emails in the browser instead of delivering them. - config.action_mailer.delivery_method = :letter_opener - - # Run jobs (including deliver_later emails) inline so letter_opener fires immediately. - config.active_job.queue_adapter = :inline - - # Make template changes take effect immediately. + # Disable caching for Action Mailer templates even if Action Controller + # caching is enabled. config.action_mailer.perform_caching = false - # Set localhost to be used by links generated in mailer templates. - config.action_mailer.default_url_options = { host: "localhost", port: ENV.fetch("PORT", 3000) } + config.action_mailer.default_url_options = { host: "localhost", port: 3000 } # Print deprecation notices to the Rails logger. config.active_support.deprecation = :log + # Raise exceptions for disallowed deprecations. + config.active_support.disallowed_deprecation = :raise + + # Tell Active Support which deprecation messages to disallow. + config.active_support.disallowed_deprecation_warnings = [] + # Raise an error on page load if there are pending migrations. config.active_record.migration_error = :page_load # Highlight code that triggered database queries in logs. 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 - # Highlight code that enqueued background job in logs. config.active_job.verbose_enqueue_logs = true - # Highlight code that triggered redirect in logs. - config.action_dispatch.verbose_redirect_logs = true - # Suppress logger output for asset requests. config.assets.quiet = true diff --git a/config/environments/production.rb b/config/environments/production.rb index f5763e0..bd30181 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -6,73 +6,89 @@ # Code is not reloaded between requests. config.enable_reloading = false - # Eager load code on boot for better performance and memory savings (ignored by Rake tasks). + # Eager load code on boot. This eager loads most of Rails and + # 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 - # Full error reports are disabled. + # Full error reports are disabled and caching is turned on. config.consider_all_requests_local = false - - # Turn on fragment caching in view templates. config.action_controller.perform_caching = true - # Cache assets for far-future expiry since they are all digest stamped. - config.public_file_server.headers = { "cache-control" => "public, max-age=#{1.year.to_i}" } + # Ensures that a master key has been made available in ENV["RAILS_MASTER_KEY"], config/master.key, or an environment + # key such as config/credentials/production.key. This key is used to decrypt credentials (and other encrypted files). + # config.require_master_key = true + + # Disable serving static files from `public/`, relying on NGINX/Apache to do so instead. + # config.public_file_server.enabled = false + + # Compress CSS using a preprocessor. + # config.assets.css_compressor = :sass + + # Do not fall back to assets pipeline if a precompiled asset is missed. + config.assets.compile = false # Enable serving of images, stylesheets, and JavaScripts from an asset server. # config.asset_host = "http://assets.example.com" + # Specifies the header that your server uses for sending files. + # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache + # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX + # Store uploaded files on the local file system (see config/storage.yml for options). config.active_storage.service = :local + # Mount Action Cable outside main process or domain. + # config.action_cable.mount_path = nil + # config.action_cable.url = "wss://example.com/cable" + # config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ] + # Assume all access to the app is happening through a SSL-terminating reverse proxy. + # Can be used together with config.force_ssl for Strict-Transport-Security and secure cookies. # config.assume_ssl = true # 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 with the current request id as a default log tag. + # Log to STDOUT by default + config.logger = ActiveSupport::Logger.new(STDOUT) + .tap { |logger| logger.formatter = ::Logger::Formatter.new } + .then { |logger| ActiveSupport::TaggedLogging.new(logger) } + + # Prepend all log lines with the following tags. config.log_tags = [ :request_id ] - config.logger = ActiveSupport::TaggedLogging.logger(STDOUT) - # Change to "debug" to log everything (including potentially personally-identifiable information!). + # "info" includes generic and useful information about system operation, but avoids logging too much + # information to avoid inadvertent exposure of personally identifiable information (PII). If you + # want to log everything, set the level to "debug". config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info") - # Prevent health checks from clogging up the logs. - config.silence_healthcheck_path = "/up" + # Use a different cache store in production. + # config.cache_store = :mem_cache_store - # Don't log any deprecations. - config.active_support.report_deprecations = false - - # Replace the default in-process memory cache store with a durable alternative. - config.cache_store = :solid_cache_store + # Use a real queuing backend for Active Job (and separate queues per environment). + # config.active_job.queue_adapter = :resque + # config.active_job.queue_name_prefix = "legacy_builder_production" - # Replace the default in-process and non-durable queuing backend for Active Job. - config.active_job.queue_adapter = :solid_queue - config.solid_queue.connects_to = { database: { writing: :queue } } + # Disable caching for Action Mailer templates even if Action Controller + # caching is enabled. + config.action_mailer.perform_caching = false # Ignore bad email addresses and do not raise email delivery errors. # Set this to true and configure the email server for immediate delivery to raise delivery errors. # config.action_mailer.raise_delivery_errors = false - # Set host to be used by links generated in mailer templates. - config.action_mailer.default_url_options = { host: "example.com" } - - # Specify outgoing SMTP server. Remember to add smtp/* credentials via bin/rails credentials:edit. - # config.action_mailer.smtp_settings = { - # user_name: Rails.application.credentials.dig(:smtp, :user_name), - # password: Rails.application.credentials.dig(:smtp, :password), - # address: "smtp.example.com", - # port: 587, - # authentication: :plain - # } - # Enable locale fallbacks for I18n (makes lookups for any locale fall back to # the I18n.default_locale when a translation cannot be found). config.i18n.fallbacks = true + # Don't log any deprecations. + config.active_support.report_deprecations = false + # Do not dump schema after migrations. config.active_record.dump_schema_after_migration = false @@ -84,7 +100,6 @@ # "example.com", # Allow requests from example.com # /.*\.example\.com/ # Allow requests from subdomains like `www.example.com` # ] - # # Skip DNS rebinding protection for the default health check endpoint. # config.host_authorization = { exclude: ->(request) { request.path == "/up" } } end diff --git a/config/environments/test.rb b/config/environments/test.rb index c2095b1..0c616a1 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -1,3 +1,5 @@ +require "active_support/core_ext/integer/time" + # The test environment is used exclusively to run your application's # test suite. You never need to work with it otherwise. Remember that # your test database is "scratch space" for the test suite and is wiped @@ -15,11 +17,12 @@ # loading is working properly before deploying your code. config.eager_load = ENV["CI"].present? - # Configure public file server for tests with cache-control for performance. - config.public_file_server.headers = { "cache-control" => "public, max-age=3600" } + # Configure public file server for tests with Cache-Control for performance. + config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{1.hour.to_i}" } - # Show full error reports. + # Show full error reports and disable caching. config.consider_all_requests_local = true + config.action_controller.perform_caching = false config.cache_store = :null_store # Render exception templates for rescuable exceptions and raise for other exceptions. @@ -31,17 +34,28 @@ # Store uploaded files on the local file system in a temporary directory. config.active_storage.service = :test + # Disable caching for Action Mailer templates even if Action Controller + # caching is enabled. + config.action_mailer.perform_caching = false + # Tell Action Mailer not to deliver emails to the real world. # The :test delivery method accumulates sent emails in the # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test - # Set host to be used by links generated in mailer templates. - config.action_mailer.default_url_options = { host: "example.com" } + # Unlike controllers, the mailer instance doesn't have any context about the + # incoming request so you'll need to provide the :host parameter yourself. + config.action_mailer.default_url_options = { host: "www.example.com" } # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr + # Raise exceptions for disallowed deprecations. + config.active_support.disallowed_deprecation = :raise + + # Tell Active Support which deprecation messages to disallow. + config.active_support.disallowed_deprecation_warnings = [] + # Raises error for missing translations. # config.i18n.raise_on_missing_translations = true diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index 4873244..bd5bcd2 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -5,3 +5,8 @@ # Add additional assets to the asset load path. # Rails.application.config.assets.paths << Emoji.images_path + +# Precompile additional assets. +# application.js, application.css, and all non-JS/CSS in the app/assets +# folder are already added. +# Rails.application.config.assets.precompile += %w[ admin.js admin.css ] diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb index d51d713..b3076b3 100644 --- a/config/initializers/content_security_policy.rb +++ b/config/initializers/content_security_policy.rb @@ -20,10 +20,6 @@ # config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s } # config.content_security_policy_nonce_directives = %w(script-src style-src) # -# # Automatically add `nonce` to `javascript_tag`, `javascript_include_tag`, and `stylesheet_link_tag` -# # if the corresponding directives are specified in `content_security_policy_nonce_directives`. -# # config.content_security_policy_nonce_auto = true -# # # Report violations without enforcing the policy. # # config.content_security_policy_report_only = true # end diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb index c0b717f..c010b83 100644 --- a/config/initializers/filter_parameter_logging.rb +++ b/config/initializers/filter_parameter_logging.rb @@ -4,5 +4,5 @@ # Use this to limit dissemination of sensitive information. # See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. Rails.application.config.filter_parameters += [ - :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn, :cvv, :cvc + :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn ] diff --git a/config/initializers/permissions_policy.rb b/config/initializers/permissions_policy.rb new file mode 100644 index 0000000..7db3b95 --- /dev/null +++ b/config/initializers/permissions_policy.rb @@ -0,0 +1,13 @@ +# Be sure to restart your server when you modify this file. + +# Define an application-wide HTTP permissions policy. For further +# information see: https://developers.google.com/web/updates/2018/06/feature-policy + +# Rails.application.config.permissions_policy do |policy| +# policy.camera :none +# policy.gyroscope :none +# policy.microphone :none +# policy.usb :none +# policy.fullscreen :self +# policy.payment :self, "https://secure.example.com" +# end diff --git a/config/puma.rb b/config/puma.rb index 38c4b86..03c166f 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -1,18 +1,13 @@ # This configuration file will be evaluated by Puma. The top-level methods that # are invoked here are part of Puma's configuration DSL. For more information # about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html. -# + # Puma starts a configurable number of processes (workers) and each process # serves each request in a thread from an internal thread pool. # -# You can control the number of workers using ENV["WEB_CONCURRENCY"]. You -# should only set this value when you want to run 2 or more workers. The -# default is already 1. You can set it to `auto` to automatically start a worker -# for each available processor. -# # The ideal number of threads per worker depends both on how much time the # application spends waiting for IO operations and on how much you wish to -# prioritize throughput over latency. +# to prioritize throughput over latency. # # As a rule of thumb, increasing the number of threads will increase how much # traffic a given process can handle (throughput), but due to CRuby's @@ -34,9 +29,6 @@ # Allow puma to be restarted by `bin/rails restart` command. plugin :tmp_restart -# Run the Solid Queue supervisor inside of Puma for single-server deployments. -plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"] - # Specify the PID file. Defaults to tmp/pids/server.pid in development. # In other environments, only set the PID file if requested. pidfile ENV["PIDFILE"] if ENV["PIDFILE"] diff --git a/config/queue.yml b/config/queue.yml deleted file mode 100644 index 9eace59..0000000 --- a/config/queue.yml +++ /dev/null @@ -1,18 +0,0 @@ -default: &default - dispatchers: - - polling_interval: 1 - batch_size: 500 - workers: - - queues: "*" - threads: 3 - processes: <%= ENV.fetch("JOB_CONCURRENCY", 1) %> - polling_interval: 0.1 - -development: - <<: *default - -test: - <<: *default - -production: - <<: *default diff --git a/config/recurring.yml b/config/recurring.yml deleted file mode 100644 index b4207f9..0000000 --- a/config/recurring.yml +++ /dev/null @@ -1,15 +0,0 @@ -# examples: -# periodic_cleanup: -# class: CleanSoftDeletedRecordsJob -# queue: background -# args: [ 1000, { batch_size: 500 } ] -# schedule: every hour -# periodic_cleanup_with_command: -# command: "SoftDeletedRecord.due.delete_all" -# priority: 2 -# schedule: at 5am every day - -production: - clear_solid_queue_finished_jobs: - command: "SolidQueue::Job.clear_finished_in_batches(sleep_between_batches: 0.3)" - schedule: every hour at minute 12 diff --git a/config/routes.rb b/config/routes.rb index a3a1873..33c9639 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,19 +1,14 @@ Rails.application.routes.draw do # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html - resource :home - resources :passwords, param: :token - resource :registration, only: %i[ new create ] - resource :email_confirmation, only: :show - resource :session # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500. # Can be used by load balancers and uptime monitors to verify that the app is live. get "up" => "rails/health#show", as: :rails_health_check - # Render dynamic PWA files from app/views/pwa/* (remember to link manifest in application.html.erb) - # get "manifest" => "rails/pwa#manifest", as: :pwa_manifest - # get "service-worker" => "rails/pwa#service_worker", as: :pwa_service_worker + # Render dynamic PWA files from app/views/pwa/* + get "service-worker" => "rails/pwa#service_worker", as: :pwa_service_worker + get "manifest" => "rails/pwa#manifest", as: :pwa_manifest # Defines the root path route ("/") - root "home#show" + # root "posts#index" end diff --git a/config/storage.yml b/config/storage.yml index 927dc53..4942ab6 100644 --- a/config/storage.yml +++ b/config/storage.yml @@ -21,6 +21,13 @@ local: # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> # bucket: your_own_bucket-<%= Rails.env %> +# Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) +# microsoft: +# service: AzureStorage +# storage_account_name: your_account_name +# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> +# container: your_container_name-<%= Rails.env %> + # mirror: # service: Mirror # primary: local diff --git a/db/cable_schema.rb b/db/cable_schema.rb deleted file mode 100644 index 2366660..0000000 --- a/db/cable_schema.rb +++ /dev/null @@ -1,11 +0,0 @@ -ActiveRecord::Schema[7.1].define(version: 1) do - create_table "solid_cable_messages", force: :cascade do |t| - t.binary "channel", limit: 1024, null: false - t.binary "payload", limit: 536870912, null: false - t.datetime "created_at", null: false - t.integer "channel_hash", limit: 8, null: false - t.index ["channel"], name: "index_solid_cable_messages_on_channel" - t.index ["channel_hash"], name: "index_solid_cable_messages_on_channel_hash" - t.index ["created_at"], name: "index_solid_cable_messages_on_created_at" - end -end diff --git a/db/cache_schema.rb b/db/cache_schema.rb deleted file mode 100644 index 81a410d..0000000 --- a/db/cache_schema.rb +++ /dev/null @@ -1,12 +0,0 @@ -ActiveRecord::Schema[7.2].define(version: 1) do - create_table "solid_cache_entries", force: :cascade do |t| - t.binary "key", limit: 1024, null: false - t.binary "value", limit: 536870912, null: false - t.datetime "created_at", null: false - t.integer "key_hash", limit: 8, null: false - t.integer "byte_size", limit: 4, null: false - t.index ["byte_size"], name: "index_solid_cache_entries_on_byte_size" - t.index ["key_hash", "byte_size"], name: "index_solid_cache_entries_on_key_hash_and_byte_size" - t.index ["key_hash"], name: "index_solid_cache_entries_on_key_hash", unique: true - end -end diff --git a/db/migrate/20260609222608_create_users.rb b/db/migrate/20260609222608_create_users.rb deleted file mode 100644 index 71f2ff1..0000000 --- a/db/migrate/20260609222608_create_users.rb +++ /dev/null @@ -1,11 +0,0 @@ -class CreateUsers < ActiveRecord::Migration[8.1] - def change - create_table :users do |t| - t.string :email_address, null: false - t.string :password_digest, null: false - - t.timestamps - end - add_index :users, :email_address, unique: true - end -end diff --git a/db/migrate/20260609222609_create_sessions.rb b/db/migrate/20260609222609_create_sessions.rb deleted file mode 100644 index ec9efdb..0000000 --- a/db/migrate/20260609222609_create_sessions.rb +++ /dev/null @@ -1,11 +0,0 @@ -class CreateSessions < ActiveRecord::Migration[8.1] - def change - create_table :sessions do |t| - t.references :user, null: false, foreign_key: true - t.string :ip_address - t.string :user_agent - - t.timestamps - end - end -end diff --git a/db/migrate/20260609225437_add_confirmed_at_to_users.rb b/db/migrate/20260609225437_add_confirmed_at_to_users.rb deleted file mode 100644 index 8ee4359..0000000 --- a/db/migrate/20260609225437_add_confirmed_at_to_users.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddConfirmedAtToUsers < ActiveRecord::Migration[8.1] - def change - add_column :users, :confirmed_at, :datetime - end -end diff --git a/db/queue_schema.rb b/db/queue_schema.rb deleted file mode 100644 index 85194b6..0000000 --- a/db/queue_schema.rb +++ /dev/null @@ -1,129 +0,0 @@ -ActiveRecord::Schema[7.1].define(version: 1) do - create_table "solid_queue_blocked_executions", force: :cascade do |t| - t.bigint "job_id", null: false - t.string "queue_name", null: false - t.integer "priority", default: 0, null: false - t.string "concurrency_key", null: false - t.datetime "expires_at", null: false - t.datetime "created_at", null: false - t.index [ "concurrency_key", "priority", "job_id" ], name: "index_solid_queue_blocked_executions_for_release" - t.index [ "expires_at", "concurrency_key" ], name: "index_solid_queue_blocked_executions_for_maintenance" - t.index [ "job_id" ], name: "index_solid_queue_blocked_executions_on_job_id", unique: true - end - - create_table "solid_queue_claimed_executions", force: :cascade do |t| - t.bigint "job_id", null: false - t.bigint "process_id" - t.datetime "created_at", null: false - t.index [ "job_id" ], name: "index_solid_queue_claimed_executions_on_job_id", unique: true - t.index [ "process_id", "job_id" ], name: "index_solid_queue_claimed_executions_on_process_id_and_job_id" - end - - create_table "solid_queue_failed_executions", force: :cascade do |t| - t.bigint "job_id", null: false - t.text "error" - t.datetime "created_at", null: false - t.index [ "job_id" ], name: "index_solid_queue_failed_executions_on_job_id", unique: true - end - - create_table "solid_queue_jobs", force: :cascade do |t| - t.string "queue_name", null: false - t.string "class_name", null: false - t.text "arguments" - t.integer "priority", default: 0, null: false - t.string "active_job_id" - t.datetime "scheduled_at" - t.datetime "finished_at" - t.string "concurrency_key" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index [ "active_job_id" ], name: "index_solid_queue_jobs_on_active_job_id" - t.index [ "class_name" ], name: "index_solid_queue_jobs_on_class_name" - t.index [ "finished_at" ], name: "index_solid_queue_jobs_on_finished_at" - t.index [ "queue_name", "finished_at" ], name: "index_solid_queue_jobs_for_filtering" - t.index [ "scheduled_at", "finished_at" ], name: "index_solid_queue_jobs_for_alerting" - end - - create_table "solid_queue_pauses", force: :cascade do |t| - t.string "queue_name", null: false - t.datetime "created_at", null: false - t.index [ "queue_name" ], name: "index_solid_queue_pauses_on_queue_name", unique: true - end - - create_table "solid_queue_processes", force: :cascade do |t| - t.string "kind", null: false - t.datetime "last_heartbeat_at", null: false - t.bigint "supervisor_id" - t.integer "pid", null: false - t.string "hostname" - t.text "metadata" - t.datetime "created_at", null: false - t.string "name", null: false - t.index [ "last_heartbeat_at" ], name: "index_solid_queue_processes_on_last_heartbeat_at" - t.index [ "name", "supervisor_id" ], name: "index_solid_queue_processes_on_name_and_supervisor_id", unique: true - t.index [ "supervisor_id" ], name: "index_solid_queue_processes_on_supervisor_id" - end - - create_table "solid_queue_ready_executions", force: :cascade do |t| - t.bigint "job_id", null: false - t.string "queue_name", null: false - t.integer "priority", default: 0, null: false - t.datetime "created_at", null: false - t.index [ "job_id" ], name: "index_solid_queue_ready_executions_on_job_id", unique: true - t.index [ "priority", "job_id" ], name: "index_solid_queue_poll_all" - t.index [ "queue_name", "priority", "job_id" ], name: "index_solid_queue_poll_by_queue" - end - - create_table "solid_queue_recurring_executions", force: :cascade do |t| - t.bigint "job_id", null: false - t.string "task_key", null: false - t.datetime "run_at", null: false - t.datetime "created_at", null: false - t.index [ "job_id" ], name: "index_solid_queue_recurring_executions_on_job_id", unique: true - t.index [ "task_key", "run_at" ], name: "index_solid_queue_recurring_executions_on_task_key_and_run_at", unique: true - end - - create_table "solid_queue_recurring_tasks", force: :cascade do |t| - t.string "key", null: false - t.string "schedule", null: false - t.string "command", limit: 2048 - t.string "class_name" - t.text "arguments" - t.string "queue_name" - t.integer "priority", default: 0 - t.boolean "static", default: true, null: false - t.text "description" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index [ "key" ], name: "index_solid_queue_recurring_tasks_on_key", unique: true - t.index [ "static" ], name: "index_solid_queue_recurring_tasks_on_static" - end - - create_table "solid_queue_scheduled_executions", force: :cascade do |t| - t.bigint "job_id", null: false - t.string "queue_name", null: false - t.integer "priority", default: 0, null: false - t.datetime "scheduled_at", null: false - t.datetime "created_at", null: false - t.index [ "job_id" ], name: "index_solid_queue_scheduled_executions_on_job_id", unique: true - t.index [ "scheduled_at", "priority", "job_id" ], name: "index_solid_queue_dispatch_all" - end - - create_table "solid_queue_semaphores", force: :cascade do |t| - t.string "key", null: false - t.integer "value", default: 1, null: false - t.datetime "expires_at", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index [ "expires_at" ], name: "index_solid_queue_semaphores_on_expires_at" - t.index [ "key", "value" ], name: "index_solid_queue_semaphores_on_key_and_value" - t.index [ "key" ], name: "index_solid_queue_semaphores_on_key", unique: true - end - - add_foreign_key "solid_queue_blocked_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade - add_foreign_key "solid_queue_claimed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade - add_foreign_key "solid_queue_failed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade - add_foreign_key "solid_queue_ready_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade - add_foreign_key "solid_queue_recurring_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade - add_foreign_key "solid_queue_scheduled_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade -end diff --git a/db/schema.rb b/db/schema.rb index a208829..f8be1d3 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,24 +10,8 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.1].define(version: 2026_06_09_225437) do - create_table "sessions", force: :cascade do |t| - t.integer "user_id", null: false - t.string "ip_address" - t.string "user_agent" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["user_id"], name: "index_sessions_on_user_id" - end +ActiveRecord::Schema[8.1].define(version: 0) do + # These are extensions that must be enabled in order to support this database + enable_extension "pg_catalog.plpgsql" - create_table "users", force: :cascade do |t| - t.string "email_address", null: false - t.string "password_digest", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.datetime "confirmed_at" - t.index ["email_address"], name: "index_users_on_email_address", unique: true - end - - add_foreign_key "sessions", "users" end diff --git a/db/seeds.rb b/db/seeds.rb index a1bd269..4fbd6ed 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -7,8 +7,3 @@ # ["Action", "Comedy", "Drama", "Horror"].each do |genre_name| # MovieGenre.find_or_create_by!(name: genre_name) # end - -user = User.find_or_initialize_by(email_address: "user@example.com") -user.password = "password" -user.confirmed_at = Time.current -user.save! diff --git a/app/assets/builds/.keep b/lib/assets/.keep similarity index 100% rename from app/assets/builds/.keep rename to lib/assets/.keep diff --git a/public/400.html b/public/400.html deleted file mode 100644 index 640de03..0000000 --- a/public/400.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - The server cannot process the request due to a client error (400 Bad Request) - - - - - - - - - - - - - -
-
- -
-
-

The server cannot process the request due to a client error. Please check the request and try again. If you're the application owner check the logs for more information.

-
-
- - - - diff --git a/public/404.html b/public/404.html index d7f0f14..2be3af2 100644 --- a/public/404.html +++ b/public/404.html @@ -1,135 +1,67 @@ - - - - - - - The page you were looking for doesn't exist (404 Not found) - - - - - - - - - - - - - -
-
- -
-
-

The page you were looking for doesn't exist. You may have mistyped the address or the page may have moved. If you're the application owner check the logs for more information.

-
-
- - - + + + + The page you were looking for doesn't exist (404) + + + + + + +
+
+

The page you were looking for doesn't exist.

+

You may have mistyped the address or the page may have moved.

+
+

If you are the application owner check the logs for more information.

+
+ diff --git a/public/406-unsupported-browser.html b/public/406-unsupported-browser.html index 43d2811..7cf1e16 100644 --- a/public/406-unsupported-browser.html +++ b/public/406-unsupported-browser.html @@ -1,135 +1,66 @@ - - - - - - - Your browser is not supported (406 Not Acceptable) - - - - - - - - - - - - - -
-
- -
-
-

Your browser is not supported.
Please upgrade your browser to continue.

-
-
- - - + + + + Your browser is not supported (406) + + + + + + +
+
+

Your browser is not supported.

+

Please upgrade your browser to continue.

+
+
+ diff --git a/public/422.html b/public/422.html index f12fb4a..c08eac0 100644 --- a/public/422.html +++ b/public/422.html @@ -1,135 +1,67 @@ - - - - - - - The change you wanted was rejected (422 Unprocessable Entity) - - - - - - - - - - - - - -
-
- -
-
-

The change you wanted was rejected. Maybe you tried to change something you didn't have access to. If you're the application owner check the logs for more information.

-
-
- - - + + + + The change you wanted was rejected (422) + + + + + + +
+
+

The change you wanted was rejected.

+

Maybe you tried to change something you didn't have access to.

+
+

If you are the application owner check the logs for more information.

+
+ diff --git a/public/500.html b/public/500.html index e4eb18a..78a030a 100644 --- a/public/500.html +++ b/public/500.html @@ -1,135 +1,66 @@ - - - - - - - We're sorry, but something went wrong (500 Internal Server Error) - - - - - - - - - - - - - -
-
- -
-
-

We're sorry, but something went wrong.
If you're the application owner check the logs for more information.

-
-
- - - + + + + We're sorry, but something went wrong (500) + + + + + + +
+
+

We're sorry, but something went wrong.

+
+

If you are the application owner check the logs for more information.

+
+ diff --git a/public/icon.png b/public/icon.png index c4c9dbf..f3b5abc 100644 Binary files a/public/icon.png and b/public/icon.png differ diff --git a/public/icon.svg b/public/icon.svg index 04b34bf..78307cc 100644 --- a/public/icon.svg +++ b/public/icon.svg @@ -1,3 +1,3 @@ - - + + diff --git a/test/application_system_test_case.rb b/test/application_system_test_case.rb new file mode 100644 index 0000000..cee29fd --- /dev/null +++ b/test/application_system_test_case.rb @@ -0,0 +1,5 @@ +require "test_helper" + +class ApplicationSystemTestCase < ActionDispatch::SystemTestCase + driven_by :selenium, using: :headless_chrome, screen_size: [ 1400, 1400 ] +end diff --git a/test/channels/application_cable/connection_test.rb b/test/channels/application_cable/connection_test.rb new file mode 100644 index 0000000..6340bf9 --- /dev/null +++ b/test/channels/application_cable/connection_test.rb @@ -0,0 +1,13 @@ +require "test_helper" + +module ApplicationCable + class ConnectionTest < ActionCable::Connection::TestCase + # test "connects with cookies" do + # cookies.signed[:user_id] = 42 + # + # connect + # + # assert_equal connection.user_id, "42" + # end + end +end diff --git a/test/controllers/email_confirmations_controller_test.rb b/test/controllers/email_confirmations_controller_test.rb deleted file mode 100644 index 88bc4bd..0000000 --- a/test/controllers/email_confirmations_controller_test.rb +++ /dev/null @@ -1,33 +0,0 @@ -require "test_helper" - -class EmailConfirmationsControllerTest < ActionDispatch::IntegrationTest - setup { @user = users(:unconfirmed) } - - test "show with a valid token confirms the user and signs them in" do - token = @user.generate_token_for(:email_confirmation) - - get email_confirmation_path(token: token) - - assert @user.reload.confirmed? - assert_redirected_to root_path - assert cookies[:session_id] - end - - test "show with an invalid token" do - get email_confirmation_path(token: "invalid") - - assert_not @user.reload.confirmed? - assert_redirected_to new_session_path - assert_nil cookies[:session_id] - end - - test "show with a token for an already-confirmed user is rejected" do - token = @user.generate_token_for(:email_confirmation) - @user.confirm! - - get email_confirmation_path(token: token) - - assert_redirected_to new_session_path - assert_nil cookies[:session_id] - end -end diff --git a/test/controllers/home_controller_test.rb b/test/controllers/home_controller_test.rb deleted file mode 100644 index f1758a1..0000000 --- a/test/controllers/home_controller_test.rb +++ /dev/null @@ -1,21 +0,0 @@ -require "test_helper" - -class HomeControllerTest < ActionDispatch::IntegrationTest - test "show is accessible when signed out and offers sign in and sign up" do - get root_path - - assert_response :success - assert_select "a[href=?]", new_session_path, text: "Sign in" - assert_select "a[href=?]", new_registration_path, text: "Sign up" - end - - test "show greets the user by email and offers sign out when signed in" do - sign_in_as(users(:one)) - - get root_path - - assert_response :success - assert_select "h1", /Hello, #{Regexp.escape(users(:one).email_address)}/ - assert_select "button", text: "Sign out" - end -end diff --git a/test/controllers/passwords_controller_test.rb b/test/controllers/passwords_controller_test.rb deleted file mode 100644 index 7fe572b..0000000 --- a/test/controllers/passwords_controller_test.rb +++ /dev/null @@ -1,67 +0,0 @@ -require "test_helper" - -class PasswordsControllerTest < ActionDispatch::IntegrationTest - setup { @user = User.take } - - test "new" do - get new_password_path - assert_response :success - end - - test "create" do - post passwords_path, params: { email_address: @user.email_address } - assert_enqueued_email_with PasswordsMailer, :reset, args: [ @user ] - assert_redirected_to new_session_path - - follow_redirect! - assert_notice "reset instructions sent" - end - - test "create for an unknown user redirects but sends no mail" do - post passwords_path, params: { email_address: "missing-user@example.com" } - assert_enqueued_emails 0 - assert_redirected_to new_session_path - - follow_redirect! - assert_notice "reset instructions sent" - end - - test "edit" do - get edit_password_path(@user.password_reset_token) - assert_response :success - end - - test "edit with invalid password reset token" do - get edit_password_path("invalid token") - assert_redirected_to new_password_path - - follow_redirect! - assert_notice "reset link is invalid" - end - - test "update" do - assert_changes -> { @user.reload.password_digest } do - put password_path(@user.password_reset_token), params: { password: "newpassword", password_confirmation: "newpassword" } - assert_redirected_to new_session_path - end - - follow_redirect! - assert_notice "Password has been reset" - end - - test "update with non matching passwords" do - token = @user.password_reset_token - assert_no_changes -> { @user.reload.password_digest } do - put password_path(token), params: { password: "no", password_confirmation: "match" } - assert_redirected_to edit_password_path(token) - end - - follow_redirect! - assert_notice "Passwords did not match" - end - - private - def assert_notice(text) - assert_select "div", /#{text}/ - end -end diff --git a/test/controllers/registrations_controller_test.rb b/test/controllers/registrations_controller_test.rb deleted file mode 100644 index 0790f37..0000000 --- a/test/controllers/registrations_controller_test.rb +++ /dev/null @@ -1,89 +0,0 @@ -require "test_helper" - -class RegistrationsControllerTest < ActionDispatch::IntegrationTest - test "new" do - get new_registration_path - assert_response :success - end - - test "create with valid params sends a confirmation email without signing in" do - assert_difference -> { User.count }, 1 do - assert_enqueued_emails 1 do - post registration_path, params: { - user: { - email_address: "new@example.com", - password: "secret123", - password_confirmation: "secret123" - } - } - end - end - - assert_redirected_to new_session_path - assert_nil cookies[:session_id] - assert_not User.find_by(email_address: "new@example.com").confirmed? - end - - test "create with the honeypot filled is silently dropped" do - assert_no_difference -> { User.count } do - assert_no_enqueued_emails do - post registration_path, params: { - nickname: "spammy mcbot", - user: { - email_address: "new@example.com", - password: "secret123", - password_confirmation: "secret123" - } - } - end - end - - # Pretends success so the bot can't detect the rejection. - assert_redirected_to new_session_path - assert_nil cookies[:session_id] - end - - test "create with too-short password" do - assert_no_difference -> { User.count } do - post registration_path, params: { - user: { - email_address: "new@example.com", - password: "short", - password_confirmation: "short" - } - } - end - - assert_response :unprocessable_entity - end - - test "create with mismatched password confirmation" do - assert_no_difference -> { User.count } do - post registration_path, params: { - user: { - email_address: "new@example.com", - password: "secret123", - password_confirmation: "nomatch" - } - } - end - - assert_response :unprocessable_entity - assert_nil cookies[:session_id] - end - - test "create with already-taken email address" do - assert_no_difference -> { User.count } do - post registration_path, params: { - user: { - email_address: users(:one).email_address, - password: "secret123", - password_confirmation: "secret123" - } - } - end - - assert_response :unprocessable_entity - assert_nil cookies[:session_id] - end -end diff --git a/test/controllers/sessions_controller_test.rb b/test/controllers/sessions_controller_test.rb deleted file mode 100644 index 73e312b..0000000 --- a/test/controllers/sessions_controller_test.rb +++ /dev/null @@ -1,40 +0,0 @@ -require "test_helper" - -class SessionsControllerTest < ActionDispatch::IntegrationTest - setup { @user = users(:one) } - - test "new" do - get new_session_path - assert_response :success - end - - test "create with valid credentials" do - post session_path, params: { email_address: @user.email_address, password: "password" } - - assert_redirected_to root_path - assert cookies[:session_id] - end - - test "create with invalid credentials" do - post session_path, params: { email_address: @user.email_address, password: "wrong" } - - assert_redirected_to new_session_path - assert_nil cookies[:session_id] - end - - test "create with unconfirmed email address" do - post session_path, params: { email_address: users(:unconfirmed).email_address, password: "password" } - - assert_redirected_to new_session_path - assert_nil cookies[:session_id] - end - - test "destroy" do - sign_in_as(@user) - - delete session_path - - assert_redirected_to new_session_path - assert_empty cookies[:session_id] - end -end diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml deleted file mode 100644 index 24d8bff..0000000 --- a/test/fixtures/users.yml +++ /dev/null @@ -1,16 +0,0 @@ -<% password_digest = BCrypt::Password.create("password") %> - -one: - email_address: one@example.com - password_digest: <%= password_digest %> - confirmed_at: <%= Time.current.to_fs(:db) %> - -two: - email_address: two@example.com - password_digest: <%= password_digest %> - confirmed_at: <%= Time.current.to_fs(:db) %> - -unconfirmed: - email_address: unconfirmed@example.com - password_digest: <%= password_digest %> - confirmed_at: <%= nil %> diff --git a/test/mailers/previews/passwords_mailer_preview.rb b/test/mailers/previews/passwords_mailer_preview.rb deleted file mode 100644 index 01d07ec..0000000 --- a/test/mailers/previews/passwords_mailer_preview.rb +++ /dev/null @@ -1,7 +0,0 @@ -# Preview all emails at http://localhost:3000/rails/mailers/passwords_mailer -class PasswordsMailerPreview < ActionMailer::Preview - # Preview this email at http://localhost:3000/rails/mailers/passwords_mailer/reset - def reset - PasswordsMailer.reset(User.take) - end -end diff --git a/test/mailers/previews/registration_mailer_preview.rb b/test/mailers/previews/registration_mailer_preview.rb deleted file mode 100644 index 1636006..0000000 --- a/test/mailers/previews/registration_mailer_preview.rb +++ /dev/null @@ -1,7 +0,0 @@ -# Preview all emails at http://localhost:3000/rails/mailers/registration_mailer -class RegistrationMailerPreview < ActionMailer::Preview - # Preview this email at http://localhost:3000/rails/mailers/registration_mailer/confirmation - def confirmation - RegistrationMailer.confirmation(User.take) - end -end diff --git a/test/models/user_test.rb b/test/models/user_test.rb deleted file mode 100644 index 83445c4..0000000 --- a/test/models/user_test.rb +++ /dev/null @@ -1,8 +0,0 @@ -require "test_helper" - -class UserTest < ActiveSupport::TestCase - test "downcases and strips email_address" do - user = User.new(email_address: " DOWNCASED@EXAMPLE.COM ") - assert_equal("downcased@example.com", user.email_address) - end -end diff --git a/script/.keep b/test/system/.keep similarity index 100% rename from script/.keep rename to test/system/.keep diff --git a/test/test_helper.rb b/test/test_helper.rb index 85c54c6..0c22470 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,7 +1,6 @@ ENV["RAILS_ENV"] ||= "test" require_relative "../config/environment" require "rails/test_help" -require_relative "test_helpers/session_test_helper" module ActiveSupport class TestCase diff --git a/test/test_helpers/session_test_helper.rb b/test/test_helpers/session_test_helper.rb deleted file mode 100644 index 0686378..0000000 --- a/test/test_helpers/session_test_helper.rb +++ /dev/null @@ -1,19 +0,0 @@ -module SessionTestHelper - def sign_in_as(user) - Current.session = user.sessions.create! - - ActionDispatch::TestRequest.create.cookie_jar.tap do |cookie_jar| - cookie_jar.signed[:session_id] = Current.session.id - cookies["session_id"] = cookie_jar[:session_id] - end - end - - def sign_out - Current.session&.destroy! - cookies.delete("session_id") - end -end - -ActiveSupport.on_load(:action_dispatch_integration_test) do - include SessionTestHelper -end