Adds a production ready docker environment (#730)

This commit is contained in:
Ivan Vergés
2024-02-12 23:50:29 +01:00
committed by GitHub
parent 202fbdd17b
commit b925110ecb
29 changed files with 460 additions and 249 deletions

View File

@ -12,6 +12,11 @@
DATABASE_USER=postgres
DATABASE_NAME=timeoverflow_development
#RAILS CONFIG
RAILS_LOG_LEVEL=debug
STORAGE_PROVIDER=amazon
FORCE_SSL=true
# Host part of the url for mail links:
MAIL_LINK_HOST=localhost:3000
MAIL_LINK_PROTO=http
@ -26,7 +31,7 @@ SMTP_PORT=587
# List of emails for superadmin users
ADMINS="admin@timeoverflow.org"
# AWS settings
# AWS settings (if STORAGE_PROVIDER=amazon)
AWS_ACCESS_KEY_ID=XXXXXXXX
AWS_SECRET_ACCESS_KEY=XXXXXXXX
AWS_BUCKET=timeoverflow_development

16
.github/workflows/docker_build.yml vendored Normal file
View File

@ -0,0 +1,16 @@
name: Docker Build
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build docker
run: docker build .
- name: Test docker compose
run: docker-compose up -d
- run: sleep 15 # wait for the server to start
- name: Check server is up
run: curl -s http://localhost:3000

1
.gitignore vendored
View File

@ -26,6 +26,7 @@ tags
.sass-cache
capybara-*.html
/vendor/bundle
/public/assets
/storage/
/coverage/
/spec/tmp/*

22
Capfile
View File

@ -1,22 +0,0 @@
# Load DSL and set up stages
require 'capistrano/setup'
# Include default deployment tasks
require 'capistrano/deploy'
# Include tasks from other gems included in your Gemfile
#
# For documentation on these, see for example:
#
# https://github.com/capistrano/rvm
# https://github.com/capistrano/rbenv
# https://github.com/capistrano/chruby
# https://github.com/capistrano/bundler
# https://github.com/capistrano/rails
# https://github.com/capistrano/passenger
#
require 'capistrano/rails'
require 'capistrano/rbenv'
# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }

100
Dockerfile Normal file
View File

@ -0,0 +1,100 @@
FROM ruby:3.2 AS builder
RUN apt-get update && apt-get upgrade -y && apt-get install -y ca-certificates curl gnupg && \
curl -fsSL https://deb.nodesource.com/setup_21.x | bash - && \
apt-get install -y \
build-essential \
nodejs \
postgresql-client \
libpq-dev && \
apt-get clean
# throw errors if Gemfile has been modified since Gemfile.lock
RUN bundle config --global frozen 1
WORKDIR /app
# Copy package dependencies files only to ensure maximum cache hit
COPY ./Gemfile /app/Gemfile
COPY ./Gemfile.lock /app/Gemfile.lock
RUN gem install bundler:$(grep -A 1 'BUNDLED WITH' Gemfile.lock | tail -n 1 | xargs) && \
bundle config --local without 'development test' && \
bundle install -j4 --retry 3 && \
# Remove unneeded gems
bundle clean --force && \
# Remove unneeded files from installed gems (cache, *.o, *.c)
rm -rf /usr/local/bundle/cache && \
find /usr/local/bundle/ -name "*.c" -delete && \
find /usr/local/bundle/ -name "*.o" -delete && \
find /usr/local/bundle/ -name ".git" -exec rm -rf {} + && \
find /usr/local/bundle/ -name ".github" -exec rm -rf {} + && \
find /usr/local/bundle/ -name "spec" -exec rm -rf {} +
# copy the rest of files
COPY ./app /app/app
COPY ./bin /app/bin
COPY ./config /app/config
COPY ./db /app/db
COPY ./lib /app/lib
COPY ./public/*.* /app/public/
COPY ./config.ru /app/config.ru
COPY ./Rakefile /app/Rakefile
# Compile assets
#
# For an app using encrypted credentials, Rails raises a `MissingKeyError`
# if the master key is missing. Because on CI there is no master key,
# we hide the credentials while compiling assets (by renaming them before and after)
#
RUN mv config/credentials.yml.enc config/credentials.yml.enc.bak 2>/dev/null || true
RUN mv config/credentials config/credentials.bak 2>/dev/null || true
RUN RAILS_ENV=production \
SECRET_KEY_BASE=dummy \
RAILS_MASTER_KEY=dummy \
DB_ADAPTER=nulldb \
bundle exec rails assets:precompile
RUN mv config/credentials.yml.enc.bak config/credentials.yml.enc 2>/dev/null || true
RUN mv config/credentials.bak config/credentials 2>/dev/null || true
RUN rm -rf tmp/cache vendor/bundle test spec .git
# This image is for production env only
FROM ruby:3.2-slim AS final
RUN apt-get update && \
apt-get install -y postgresql-client \
imagemagick \
libvips \
curl \
supervisor && \
apt-get clean
EXPOSE 3000
ENV RAILS_LOG_TO_STDOUT true
ENV RAILS_SERVE_STATIC_FILES true
ENV RAILS_ENV production
ARG RUN_RAILS
ARG RUN_SIDEKIQ
# Add user
RUN addgroup --system --gid 1000 app && \
adduser --system --uid 1000 --home /app --group app
WORKDIR /app
COPY ./entrypoint.sh /app/entrypoint.sh
COPY ./supervisord.conf /etc/supervisord.conf
COPY --from=builder --chown=app:app /usr/local/bundle/ /usr/local/bundle/
COPY --from=builder --chown=app:app /app /app
USER app
HEALTHCHECK --interval=1m --timeout=5s --start-period=10s \
CMD (curl -IfSs http://localhost:3000/ -A "HealthCheck: Docker/1.0") || exit 1
ENTRYPOINT ["/app/entrypoint.sh"]
CMD ["/usr/bin/supervisord"]

13
Gemfile
View File

@ -13,7 +13,6 @@ gem 'json_translate', '~> 4.0.0'
gem 'devise', '~> 4.9.1'
gem 'devise-i18n', '~> 1.11.0'
gem 'http_accept_language', '~> 2.1.1'
gem 'unicorn', '~> 6.1.0'
gem 'kaminari', '~> 1.2.1'
gem 'simple_form', '~> 5.0.2'
gem 'rollbar', '~> 3.4'
@ -26,21 +25,24 @@ gem 'sidekiq-cron', '~> 1.9.1'
gem 'aws-sdk-s3', '~> 1.94', require: false
gem 'image_processing', '~> 1.12'
gem 'active_storage_validations', '~> 1.1.3'
gem "puma", ">= 5.0.0"
gem 'matrix', '~> 0.4.1'
# Assets
gem 'jquery-rails', '~> 4.4.0'
gem 'bootstrap-sass', '~> 3.4'
gem 'sassc-rails', '~> 2.1.2'
gem 'uglifier', '~> 4.2.0'
gem 'select2-rails', '~> 4.0.13'
group :production do
# we are using an ExecJS runtime only on the precompilation phase
gem "uglifier", "~> 4.2.0", require: false
end
group :development do
gem 'localeapp', '~> 3.3', require: false
gem 'letter_opener', '~> 1.7.0'
gem 'web-console', '~> 4.1.0'
gem 'capistrano', '~> 3.15.0'
gem 'capistrano-rails', '~> 1.1'
gem 'capistrano-rbenv', '~> 2.1'
end
group :development, :test do
@ -60,5 +62,4 @@ group :test do
gem 'capybara', '~> 3.29'
gem 'selenium-webdriver', '~> 4.16'
gem 'simplecov', '~> 0.22', require: false
gem 'webrick'
end

View File

@ -46,7 +46,7 @@ GEM
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
active_storage_validations (1.1.3)
active_storage_validations (1.1.4)
activejob (>= 5.2.0)
activemodel (>= 5.2.0)
activestorage (>= 5.2.0)
@ -82,8 +82,6 @@ GEM
tzinfo (~> 2.0)
addressable (2.8.6)
public_suffix (>= 2.0.2, < 6.0)
airbrussh (1.5.0)
sshkit (>= 1.6.1, != 1.7.0)
arbre (1.7.0)
activesupport (>= 3.0.0)
ruby2_keywords (>= 0.0.2)
@ -91,53 +89,41 @@ GEM
autoprefixer-rails (10.4.16.0)
execjs (~> 2)
aws-eventstream (1.3.0)
aws-partitions (1.864.0)
aws-sdk-core (3.190.0)
aws-partitions (1.887.0)
aws-sdk-core (3.191.0)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.8)
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.74.0)
aws-sdk-core (~> 3, >= 3.188.0)
aws-sdk-kms (1.77.0)
aws-sdk-core (~> 3, >= 3.191.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.141.0)
aws-sdk-core (~> 3, >= 3.189.0)
aws-sdk-s3 (1.143.0)
aws-sdk-core (~> 3, >= 3.191.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.8)
aws-sigv4 (1.8.0)
aws-eventstream (~> 1, >= 1.0.2)
base64 (0.2.0)
bcrypt (3.1.20)
bindex (0.8.1)
bootsnap (1.17.0)
bootsnap (1.18.3)
msgpack (~> 1.2)
bootstrap-sass (3.4.1)
autoprefixer-rails (>= 5.2.1)
sassc (>= 2.0.0)
builder (3.2.4)
byebug (11.1.3)
capistrano (3.15.0)
airbrussh (>= 1.0.0)
i18n
rake (>= 10.0.0)
sshkit (>= 1.9.0)
capistrano-bundler (2.1.0)
capistrano (~> 3.1)
capistrano-rails (1.6.3)
capistrano (~> 3.1)
capistrano-bundler (>= 1.1, < 3)
capistrano-rbenv (2.2.0)
capistrano (~> 3.1)
sshkit (~> 1.3)
capybara (3.39.2)
capybara (3.40.0)
addressable
matrix
mini_mime (>= 0.1.3)
nokogiri (~> 1.8)
nokogiri (~> 1.11)
rack (>= 1.6.0)
rack-test (>= 0.6.3)
regexp_parser (>= 1.5, < 3.0)
xpath (~> 3.2)
concurrent-ruby (1.2.2)
concurrent-ruby (1.2.3)
connection_pool (2.4.1)
crass (1.0.6)
database_cleaner (2.0.2)
@ -155,9 +141,9 @@ GEM
warden (~> 1.2.3)
devise-i18n (1.11.1)
devise (>= 4.9.0)
diff-lcs (1.5.0)
diff-lcs (1.5.1)
docile (1.4.0)
domain_name (0.6.20231109)
domain_name (0.6.20240107)
dotenv (2.7.6)
dotenv-rails (2.7.6)
dotenv (= 2.7.6)
@ -216,7 +202,6 @@ GEM
activerecord
kaminari-core (= 1.2.2)
kaminari-core (1.2.2)
kgio (2.11.4)
language_server-protocol (3.17.0.3)
launchy (2.5.2)
addressable (~> 2.8)
@ -238,34 +223,29 @@ GEM
marcel (1.0.2)
matrix (0.4.2)
method_source (1.0.0)
mime-types (3.5.1)
mime-types (3.5.2)
mime-types-data (~> 3.2015)
mime-types-data (3.2023.1205)
mini_magick (4.12.0)
mini_mime (1.1.5)
mini_portile2 (2.8.5)
minitest (5.20.0)
minitest (5.22.0)
msgpack (1.7.2)
net-imap (0.4.7)
net-imap (0.4.10)
date
net-protocol
net-pop (0.1.2)
net-protocol
net-protocol (0.2.2)
timeout
net-scp (4.0.0)
net-ssh (>= 2.6.5, < 8.0.0)
net-smtp (0.4.0)
net-smtp (0.4.0.1)
net-protocol
net-ssh (7.2.0)
netrc (0.11.0)
nio4r (2.7.0)
nokogiri (1.15.5)
mini_portile2 (~> 2.8.2)
nokogiri (1.16.2-x86_64-linux)
racc (~> 1.4)
orm_adapter (0.5.0)
parallel (1.23.0)
parser (3.2.2.4)
parallel (1.24.0)
parser (3.3.0.5)
ast (~> 2.4.1)
racc
pdf-core (0.9.0)
@ -279,6 +259,8 @@ GEM
prawn-table (0.2.2)
prawn (>= 1.3.0, < 3.0.0)
public_suffix (5.0.4)
puma (6.4.2)
nio4r (~> 2.0)
pundit (2.1.1)
activesupport (>= 3.0.0)
raabro (1.4.0)
@ -322,15 +304,14 @@ GEM
thor (~> 1.0)
zeitwerk (~> 2.5)
rainbow (3.1.1)
raindrops (0.20.1)
rake (13.1.0)
ransack (3.2.1)
activerecord (>= 6.1.5)
activesupport (>= 6.1.5)
i18n
rdiscount (2.2.7.1)
rdiscount (2.2.7.3)
redis (4.8.1)
regexp_parser (2.8.3)
regexp_parser (2.9.0)
responders (3.1.1)
actionpack (>= 5.2)
railties (>= 5.2)
@ -340,16 +321,16 @@ GEM
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
rexml (3.2.6)
rollbar (3.4.2)
rspec-core (3.12.2)
rspec-support (~> 3.12.0)
rspec-expectations (3.12.3)
rollbar (3.5.1)
rspec-core (3.13.0)
rspec-support (~> 3.13.0)
rspec-expectations (3.13.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-mocks (3.12.6)
rspec-support (~> 3.13.0)
rspec-mocks (3.13.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-rails (6.1.0)
rspec-support (~> 3.13.0)
rspec-rails (6.1.1)
actionpack (>= 6.1)
activesupport (>= 6.1)
railties (>= 6.1)
@ -357,12 +338,12 @@ GEM
rspec-expectations (~> 3.12)
rspec-mocks (~> 3.12)
rspec-support (~> 3.12)
rspec-support (3.12.1)
rubocop (1.58.0)
rspec-support (3.13.0)
rubocop (1.60.2)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
parser (>= 3.2.2.4)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
@ -371,7 +352,7 @@ GEM
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.30.0)
parser (>= 3.2.1.0)
rubocop-rails (2.22.2)
rubocop-rails (2.23.1)
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 1.33.0, < 2.0)
@ -390,7 +371,8 @@ GEM
sprockets-rails
tilt
select2-rails (4.0.13)
selenium-webdriver (4.16.0)
selenium-webdriver (4.17.0)
base64 (~> 0.2)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
@ -412,7 +394,7 @@ GEM
simplecov_json_formatter (~> 0.1)
simplecov-html (0.12.3)
simplecov_json_formatter (0.1.4)
skylight (6.0.1)
skylight (6.0.3)
activesupport (>= 5.2.0)
sprockets (4.2.1)
concurrent-ruby (~> 1.0)
@ -421,9 +403,6 @@ GEM
actionpack (>= 5.2)
activesupport (>= 5.2)
sprockets (>= 3.0.0)
sshkit (1.21.6)
net-scp (>= 1.1.2)
net-ssh (>= 2.8.0)
thor (1.3.0)
tilt (2.3.0)
timeout (0.4.1)
@ -433,9 +412,6 @@ GEM
uglifier (4.2.0)
execjs (>= 0.3.0, < 3)
unicode-display_width (2.5.0)
unicorn (6.1.0)
kgio (~> 2.6)
raindrops (~> 0.7)
warden (1.2.9)
rack (>= 2.0.9)
web-console (4.1.0)
@ -443,7 +419,6 @@ GEM
activemodel (>= 6.0.0)
bindex (>= 0.4.0)
railties (>= 6.0.0)
webrick (1.8.1)
websocket (1.2.10)
websocket-driver (0.7.6)
websocket-extensions (>= 0.1.0)
@ -453,7 +428,7 @@ GEM
zeitwerk (2.6.12)
PLATFORMS
ruby
x86_64-linux
DEPENDENCIES
active_storage_validations (~> 1.1.3)
@ -462,9 +437,6 @@ DEPENDENCIES
bootsnap (~> 1.12)
bootstrap-sass (~> 3.4)
byebug (~> 11.0)
capistrano (~> 3.15.0)
capistrano-rails (~> 1.1)
capistrano-rbenv (~> 2.1)
capybara (~> 3.29)
database_cleaner (~> 2.0)
devise (~> 4.9.1)
@ -480,10 +452,12 @@ DEPENDENCIES
kaminari (~> 1.2.1)
letter_opener (~> 1.7.0)
localeapp (~> 3.3)
matrix (~> 0.4.1)
pg (~> 1.4)
pg_search (~> 2.3.5)
prawn (~> 2.4.0)
prawn-table (~> 0.2.2)
puma (>= 5.0.0)
pundit (~> 2.1.0)
rails (~> 7.0.8)
rails-controller-testing
@ -504,9 +478,7 @@ DEPENDENCIES
simplecov (~> 0.22)
skylight (~> 6.0)
uglifier (~> 4.2.0)
unicorn (~> 6.1.0)
web-console (~> 4.1.0)
webrick
BUNDLED WITH
2.1.4
2.4.10

View File

@ -41,6 +41,65 @@ as well as being able to post offers / demand ads explained in detail.
On the other hand the members can be paid the services of virtual way to save the passage through the office of the Bank
of Time and also have the possibility to consult the extract of their account.
## Docker deploying
This site is ready to be deployed in production with an optimized Docker image which uses two stages in order to minimize the final image size.
You can locally test the production deployment by the include docker-compose.yml file:
```bash
docker-compose up
```
The first time running it will build the image and setup the database along with some seeds in it (testing data). If the database already exists, it will run migrations (if needed) and just start the application.
Go to `http://localhost:3000` to see the application running.
> Note that the current docker-compose.yml is not suitable for a real production deployment, it's just for testing the production Dockerfile locally.
> For production deployment you should use a real database and a reverse proxy like Nginx or Apache (with SSL enabled).
> Refer to the next section in order to see the relevant ENV variables to configure the application.
### ENV variables
In order to configure the application you can use the following ENV variables:
> Make sure to configure at least the ones without a default value (empty).
| ENV | Description | Default |
| --- | --- | --- |
| `ALLOWED_HOSTS` | Put here the list of hosts allowed to access the application. Separate with spaces, for instance: `www.timeoverflow.org timeoverflow.org` | `localhost` |
| `RAILS_ENV` | Define the rails environment (not necessary to setup unless you have some special requirements) | `production` |
| `SECRET_KEY_BASE` | Secret key for the application, generate a new one with the command `rails secret` | |
| `DATABASE_URL` | Database URL, the format is `postgresql://user:password@host:port/database` | |
| `RAILS_SERVE_STATIC_FILES` | Tell the application to serve static files (you might want to turn this off if you are using an external web server to serve files from the `public` folder) | `true` |
| `RAILS_LOG_TO_STDOUT` | Tell the application to log to STDOUT (useful for Docker) | `true` |
| `RAILS_LOG_LEVEL` | Log level for the application (use `debug` for maximum information) | `info` |
| `RAILS_MAX_THREADS` | Maximum number of threads to use in the application (use `1` if multithreading is not desired) | `5` |
| `RAILS_MIN_THREADS` | Minimum number of threads to use in the application | `RAILS_MAX_THREADS` value |
| `WEB_CONCURRENCY` | Number of web server processes to use | `2` |
| `RUN_SIDEKIQ` | Run Sidekiq worker process in the docker instance (you might want to change this if want to run different docker instances for Sidekiq and Rails) | `true` |
| `RUN_RAILS` | Run Rails web server process in the docker instance | `true` |
| `QUEUE_ADAPTER` | Adapter to use for background jobs (currently the application is using exclusively Sidekiq, so no other options here right now) | `sidekiq` |
| `SIDEKIQ_CONCURRENCY` | Number of threads to use in Sidekiq | `5` |
| `STORAGE_PROVIDER` | Storage provider for the application (currently the application supports `local` and `amazon`) | `local` |
| `FORCE_SSL` | Force SSL connections | `false` |
| `MAIL_LINK_HOST` | Host to use in the links sent by email (use your domain without protocol `mydomain.tld`) | |
| `MAIL_LINK_PROTOCOL` | Protocol to use in the previous host defined for links sent by email | `https` |
| `SMTP_ADDRESS` | SMTP server address (ie: `smtp.mailgun.org`) | |
| `SMTP_PORT` | SMTP server port (ie: `587`) | |
| `SMTP_DOMAIN` | SMTP domain (usually the application's domain) | |
| `SMTP_USER_NAME` | SMTP username | |
| `SMTP_PASSWORD` | SMTP password | |
| `SMTP_AUTHENTICATION` | SMTP authentication method | `plain` |
| `SMTP_ENABLE_STARTTLS_AUTO` | Enable STARTTLS | `true` |
| `SMTP_OPENSSL_VERIFY_MODE` | OpenSSL verify mode | `none` |
| `AWS_ACCESS_KEY_ID` | AWS access key ID (only if `STORAGE_PROVIDER` is `amazon`) | |
| `AWS_SECRET_ACCESS_KEY` | AWS secret access key (only if `STORAGE_PROVIDER` is `amazon`) | |
| `AWS_BUCKET` | AWS bucket name (only if `STORAGE_PROVIDER` is `amazon`) | |
| `AWS_REGION` | AWS region (only if `STORAGE_PROVIDER` is `amazon`) | |
| `ADMINS` | Space separated list of emails for the superadmins (ie: `admin@timeoverflow.org` | |
## Contributions
**Join our collaborators team!**

View File

@ -5,7 +5,7 @@ ActiveAdmin.register Organization do
output = tag.p organization.name
if organization.logo.attached?
output << image_tag(organization.logo.variant(resize: "40^x"))
output << image_tag(organization.logo.variant(resize_to_fill: [40, nil]))
end
output.html_safe
@ -29,7 +29,7 @@ ActiveAdmin.register Organization do
show do
div do
if organization.logo.attached?
image_tag(organization.logo.variant(resize: "100^x"))
image_tag(organization.logo.variant(resize_to_fill: [100, nil]))
end
end
default_main_content

View File

@ -1,7 +1,7 @@
//= require jquery2
//= require jquery_ujs
//= require jquery.validate
//= require vendor/jquery.validate
//= require bootstrap
//= require highcharts
//= require highcharts-exporting
//= require vendor/highcharts
//= require vendor/highcharts-exporting
//= require select2

View File

@ -8,7 +8,7 @@ module ApplicationHelper
def avatar_url(user, size = 32)
user.avatar.attached? ?
user.avatar.variant(resize: "#{size}x#{size}") :
user.avatar.variant(resize_to_fit: [size, size]) :
gravatar_url(user, size)
end
@ -31,7 +31,7 @@ module ApplicationHelper
return if "#{controller_name}##{action_name}".in? %w(organizations#index pages#show)
content_tag(:div, class: "row organization-logo") do
image_tag org.logo.variant(resize: "x200^")
image_tag org.logo.variant(resize_to_fit: [200, nil])
end
end

View File

@ -23,17 +23,16 @@ module Timeoverflow
# SKYLIGHT_AUTHENTICATION env var for this to work.
config.skylight.environments += ["staging"]
# ActiveJob configuration
config.active_job.queue_adapter = :sidekiq
# Use db/structure.sql with SQL as schema format
# This is needed to store in the schema SQL statements not covered by the ORM
config.active_record.schema_format = :sql
# Guard against DNS rebinding attacks by permitting hosts
config.hosts << 'timeoverflow.local'
config.hosts << 'staging.timeoverflow.org'
config.hosts << 'www.timeoverflow.org'
config.hosts << 'timeoverflow.org'
# localhost is necessary for the docker image
config.hosts = ENV.fetch('ALLOWED_HOSTS', 'localhost').split(' ')
# config.hosts << 'timeoverflow.local'
# config.hosts << 'staging.timeoverflow.org'
# config.hosts << 'www.timeoverflow.org'
# config.hosts << 'timeoverflow.org'
end
end

View File

@ -1,8 +1,14 @@
defaults: &defaults
adapter: postgresql
username: <%= ENV['DATABASE_USER'] || ENV["POSTGRES_USER"] %>
username: <%= ENV['DATABASE_USER'] || ENV["POSTGRES_USER"] || ENV["DATABASE_USERNAME"] %>
password: <%= ENV['DATABASE_PASSWORD'] || ENV["POSTGRES_PASSWORD"] %>
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
host: <%= ENV.fetch("DATABASE_HOST") { "localhost" } %>
port: <%= ENV.fetch("DATABASE_PORT") { "5432" } %>
template: 'template0'
encoding: 'UTF8'
encoding: unicode
# For details on connection pooling, see rails configuration guide
# http://guides.rubyonrails.org/configuring.html#database-pooling
development:
<<: *defaults
@ -16,12 +22,9 @@ test:
staging:
<<: *defaults
collation: 'es_ES.UTF-8'
ctype: 'es_ES.UTF-8'
database: <%= ENV.fetch('DATABASE_NAME', 'timeoverflow_staging') %>
production:
<<: *defaults
collation: 'es_ES.UTF-8'
ctype: 'es_ES.UTF-8'
database: <%= ENV.fetch('DATABASE_NAME', 'timeoverflow_production') %>
<%= "url: #{ENV['DATABASE_URL']}" if ENV['DATABASE_URL'].present? %>
<%= "database: #{ENV.fetch('DATABASE_NAME', 'timeoverflow_production')}" unless ENV['DATABASE_URL'].present? %>

View File

@ -1,81 +0,0 @@
# config valid only for current version of Capistrano
lock '3.15.0'
set :application, 'timeoverflow'
set :repo_url, 'git@github.com:coopdevs/timeoverflow.git'
set :rbenv_type, :user
# Default branch is :master
ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp
# Default deploy_to directory is /var/www/my_app_name
# set :deploy_to, '/var/www/my_app_name'
# Default value for :scm is :git
# set :scm, :git
# Default value for :format is :pretty
# set :format, :pretty
# Default value for :log_level is :debug
# set :log_level, :debug
# Default value for :pty is false
# set :pty, true
# Default value for :linked_files is []
set :linked_files, fetch(:linked_files, []).push(
# "config/database.yml",
# "config/secrets.yml",
# ".env",
)
# Default value for linked_dirs is []
set :linked_dirs, fetch(:linked_dirs, []).push(
'log',
'tmp/pids',
'tmp/cache',
'tmp/sockets',
'vendor/bundle',
'public/system'
)
# Default value for default_env is {}
# set :default_env, { path: "/opt/ruby/bin:$PATH" }
# Default value for keep_releases is 5
# set :keep_releases, 5
namespace :unicorn do
desc 'reload Unicorn'
task :reload do
on roles(:app) do
execute "sudo systemctl reload timeoverflow"
end
end
end
namespace :sidekiq do
desc 'reload Sidekiq'
task :restart do
on roles(:app) do
execute "sudo systemctl restart sidekiq"
end
end
end
task "deploy:db:load" do
on primary :db do
within release_path do
with rails_env: fetch(:rails_env) do
execute :rake, "db:schema:load"
end
end
end
end
before "deploy:migrate", "deploy:db:load" if ENV["COLD"]
after "deploy:finishing", "unicorn:reload"
after "deploy:finishing", "sidekiq:restart"

View File

@ -1 +0,0 @@
server 'www.timeoverflow.org', user: 'timeoverflow', roles: %w(app db web)

View File

@ -1 +0,0 @@
server 'staging.timeoverflow.org', user: 'timeoverflow', roles: %w(app db web)

View File

@ -1,3 +1,6 @@
# Uglifier is only used on the precompile phase, so we can require it conditionally
require "uglifier" if ENV["SECRET_KEY_BASE"] == "dummy"
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
@ -20,28 +23,30 @@ Rails.application.configure do
# Disable serving static files from the `/public` folder by default since
# Apache or NGINX already handles this.
config.public_file_server.enabled = true
config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present?
# Compress CSS & JS using preprocessors.
config.assets.js_compressor = Uglifier.new(harmony: true)
config.assets.css_compressor = :sass
# Compress CSS & JS using preprocessors only on the precompile phase
if ENV["SECRET_KEY_BASE"] == "dummy"
config.assets.js_compressor = Uglifier.new(harmony: true)
config.assets.css_compressor = :sass
end
# Do not fallback to assets pipeline if a precompiled asset is missed.
config.assets.compile = true
config.assets.compile = false
# Asset digests allow you to set far-future HTTP expiration dates on all assets,
# yet still be able to expire them through the digest params.
config.assets.digest = true
# Enable serving of images, stylesheets, and JavaScripts from an asset server.
# config.action_controller.asset_host = 'http://assets.example.com'
config.asset_host = ENV.fetch("RAILS_ASSET_HOST", nil) if ENV["RAILS_ASSET_HOST"].present?
# 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 = :amazon
config.active_storage.service = ENV.fetch("STORAGE_PROVIDER", :local)
# Mount Action Cable outside main process or domain.
# config.action_cable.mount_path = nil
@ -49,20 +54,24 @@ Rails.application.configure do
# config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
config.force_ssl = true
config.force_ssl = ENV.fetch("FORCE_SSL", "true") == "true"
# Use the lowest log level to ensure availability of diagnostic information
# when problems arise.
config.log_level = :debug
config.log_level = if %w(debug info warn error fatal).include?(ENV.fetch("RAILS_LOG_LEVEL", nil))
ENV["RAILS_LOG_LEVEL"]
else
:info
end
# Prepend all log lines with the following tags.
config.log_tags = [ :request_id ]
config.log_tags = [:request_id]
# Use a different cache store in production.
# config.cache_store = :mem_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_adapter = :sidekiq
# config.active_job.queue_name_prefix = "timeoverflow_production"
config.action_mailer.perform_caching = false
@ -74,10 +83,10 @@ Rails.application.configure do
config.action_mailer.delivery_method = :smtp
config.action_mailer.default_url_options = {
host: ENV["MAIL_LINK_HOST"],
protocol: (ENV["MAIL_LINK_PROTO"] || "https")
protocol: ENV["MAIL_LINK_PROTO"] || "https"
}
smtp_env = Hash[ENV.map do |k,v|
smtp_env = Hash[ENV.map do |k, v|
if /^SMTP_(.*)$/ === k
[$1.downcase.to_sym, YAML.load(v)]
end

47
config/puma.rb Normal file
View File

@ -0,0 +1,47 @@
# frozen_string_literal: true
# Puma can serve each request in a thread from an internal thread pool.
# The `threads` method setting takes two numbers: a minimum and maximum.
# Any libraries that use thread pools should be configured to match
# the maximum value specified for Puma. Default is set to 5 threads for minimum
# and maximum; this matches the default thread size of Active Record.
#
max_threads_count = ENV.fetch("RAILS_MAX_THREADS", 5)
min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
threads min_threads_count, max_threads_count
# Specifies the `worker_timeout` threshold that Puma will use to wait before
# terminating a worker in development environments.
#
worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development"
# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
#
port ENV.fetch("PORT", 3000)
# Specifies the `environment` that Puma will run in.
#
environment ENV.fetch("RAILS_ENV", "development")
# Specifies the `pidfile` that Puma will use.
pidfile ENV.fetch("PIDFILE", "tmp/pids/server.pid")
# Specifies the number of `workers` to boot in clustered mode.
# Workers are forked web server processes. If using threads and workers together
# the concurrency of the application would be max `threads` * `workers`.
# Workers do not work on JRuby or Windows (both of which do not support
# processes).
#
if ENV.fetch("RAILS_ENV") == "production"
workers ENV.fetch("WEB_CONCURRENCY", 2)
# Use the `preload_app!` method when specifying a `workers` number.
# This directive tells Puma to first boot the application and load code
# before forking the application. This takes advantage of Copy On Write
# process behavior so workers use less memory.
#
preload_app!
else
# Allow puma to be restarted by `rails restart` command.
plugin :tmp_restart
end

View File

@ -1,3 +1,4 @@
:concurrency: <%= ENV.fetch("SIDEKIQ_CONCURRENCY", "5").to_i %>
:queues:
- default
- cron

View File

@ -1,29 +0,0 @@
app_path = File.expand_path(File.dirname(__FILE__) + '/..')
worker_processes 2
listen 8080, tcp_nopush: true
working_directory app_path
pid app_path + '/tmp/unicorn.pid'
stderr_path '/var/www/timeoverflow/shared/log/unicorn.err.log'
stdout_path '/var/www/timeoverflow/shared/log/unicorn.std.log'
# Load the app up before forking
# Combine Ruby 2.0.0+ with "preload_app true" for memory savings
preload_app true
before_fork do |server, worker|
# the following is highly recomended for Rails + "preload_app true"
# as there's no need for the master process to hold a connection
defined?(ActiveRecord::Base) and
ActiveRecord::Base.connection.disconnect!
end
after_fork do |server, worker|
# the following is *required* for Rails + "preload_app true"
defined?(ActiveRecord::Base) and
ActiveRecord::Base.establish_connection
end

49
docker-compose.yml Normal file
View File

@ -0,0 +1,49 @@
version: '3'
services:
app:
build: .
volumes:
- ./entrypoint.sh:/app/entrypoint.sh
- ./db/seeds.rb:/app/db/seeds.rb
- ./storage:/app/storage
environment:
- DATABASE_URL=postgres://postgres:timeoverflow@db/timeoverflow
- SECRET_KEY_BASE=d2a645fb46fbd3d4380fb22230ddea4062570eb00853ca5dfe97f8bb1cbff1ad6891c573a4b4b06beb2d0baf59afc9e00794314490a644fc5808ad6cbc3a6379
- FORCE_SSL=false
- RAILS_LOG_LEVEL=debug
- REDIS_URL=redis://redis:6379/0
- ADMINS=admin@timeoverflow.org
ports:
- 3000:3000
depends_on:
- db
- redis
sidekiq:
build: .
volumes:
- ./entrypoint.sh:/app/entrypoint.sh
- ./storage:/app/storage
environment:
- DATABASE_URL=postgres://postgres:timeoverflow@db/timeoverflow
- SECRET_KEY_BASE=d2a645fb46fbd3d4380fb22230ddea4062570eb00853ca5dfe97f8bb1cbff1ad6891c573a4b4b06beb2d0baf59afc9e00794314490a644fc5808ad6cbc3a6379
- FORCE_SSL=false
- RAILS_LOG_LEVEL=debug
- REDIS_URL=redis://redis:6379/0
- ADMINS=admin@timeoverflow.org
- RUN_SIDEKIQ=true
depends_on:
- db
- redis
db:
image: postgres:14-alpine
environment:
- POSTGRES_PASSWORD=timeoverflow
volumes:
- pg_data:/var/lib/postgresql/data
redis:
image: redis
volumes:
- redis_data:/data
volumes:
pg_data:
redis_data:

58
entrypoint.sh Executable file
View File

@ -0,0 +1,58 @@
#!/bin/bash
# Run rails by default if sidekiq is specified
if [ -z "$RUN_RAILS" ] && [ -z "$RUN_SIDEKIQ" ]; then
RUN_RAILS=true
echo "⚠️ RUN_RAILS and RUN_SIDEKIQ are not set, defaulting to RUN_RAILS=true, RUN_SIDEKIQ=false"
fi
# ensure booleans
if [ "$RUN_RAILS" == "true" ] || [ "$RUN_RAILS" == "1" ]; then
RUN_RAILS=true
else
RUN_RAILS=false
fi
if [ "$RUN_SIDEKIQ" == "true" ] || [ "$RUN_SIDEKIQ" == "1" ]; then
RUN_SIDEKIQ=true
else
RUN_SIDEKIQ=false
fi
if [ "$RUN_RAILS" == "true" ]; then
echo "✅ Running Rails"
fi
if [ "$RUN_SIDEKIQ" == "true" ]; then
echo "✅ Running Sidekiq"
fi
export RUN_RAILS
export RUN_SIDEKIQ
# Check all the gems are installed or fails.
bundle check
if [ $? -ne 0 ]; then
echo "❌ Gems in Gemfile are not installed, aborting..."
exit 1
else
echo "✅ Gems in Gemfile are installed"
fi
# if no database, run setup
if [ -z "$SKIP_DATABASE_SETUP" ]; then
bundle exec rails db:setup
else
echo "⚠️ Skipping database setup"
fi
# Check no migrations are pending migrations
if [ -z "$SKIP_MIGRATIONS" ]; then
bundle exec rails db:migrate
else
echo "⚠️ Skipping migrations"
fi
echo "✅ Migrations are all up"
echo "🚀 $@"
exec "$@"

View File

@ -10,7 +10,7 @@ RSpec.describe ApplicationHelper do
img = helper.avatar_url(user, 50)
expect(img.class).to eq(ActiveStorage::VariantWithRecord)
expect(img.variation.transformations[:resize]).to eq("50x50")
expect(img.variation.transformations[:resize_to_fit]).to eq([50, 50])
expect(img.blob.filename).to eq("name.png")
end

View File

@ -21,7 +21,7 @@ require 'selenium/webdriver'
require 'faker'
require 'shoulda/matchers'
Capybara.server = :webrick
Capybara.server = :puma
Capybara.register_driver :headless_chrome do |app|
browser_options = Selenium::WebDriver::Chrome::Options.new(
args: %w(headless disable-gpu no-sandbox)

25
supervisord.conf Normal file
View File

@ -0,0 +1,25 @@
[supervisord]
nodaemon=true
logfile=/tmp/supervisord.log
pidfile=/tmp/supervisord.pid
[program:puma]
command=bin/rails server -b 0.0.0.0
directory=/app
# If RUN_RAILS is not set, defaults to RUN_SIDEKIQ not being defined
autostart=%(ENV_RUN_RAILS)s
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
[program:sidekiq]
command=bundle exec sidekiq -C config/sidekiq.yml
directory=/app
autostart=%(ENV_RUN_SIDEKIQ)s
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
[supervisorctl]

View File