From fb6a6755ae6a1177125f0cbc51ce14a49b323d11 Mon Sep 17 00:00:00 2001 From: Andy Pfister Date: Tue, 14 Jan 2025 15:11:40 +0100 Subject: [PATCH] somehow make it run in Docker --- .dockerignore | 3 + Dockerfile | 37 ++++ Gemfile | 9 +- Gemfile.lock | 31 +-- app/controllers/api/v1/binaries_controller.rb | 2 +- app/mailers/application_mailer.rb | 4 + bin/start.sh | 13 ++ bin/wait-for-it.sh | 182 ++++++++++++++++++ config/application.rb | 2 +- config/cable.yml | 7 +- config/database.yml | 5 + config/environments/production.rb | 10 +- docker-compose.yml | 11 ++ .../api/v1/binaries_controller_test.rb | 2 +- test/factories/binaries.rb | 2 +- 15 files changed, 276 insertions(+), 44 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 app/mailers/application_mailer.rb create mode 100755 bin/start.sh create mode 100755 bin/wait-for-it.sh create mode 100644 docker-compose.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..4212ed5 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +node_modules +storage +.git diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f58d178 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,37 @@ +FROM ruby:3.4.1-alpine AS base + +WORKDIR /work + +ENV RAILS_ENV="production" \ + BUNDLE_PATH="/usr/local/bundle" \ + BUNDLE_WITHOUT="development test" + +RUN apk add -U --no-cache build-base freetds-dev npm wget yaml-dev + +COPY Gemfile Gemfile.lock . + +RUN bundle config build.tiny_tds --with-freetds-dir=/usr && \ + bundle install -j $(nproc) + +COPY . . + +RUN npm i && \ + bundle exec rails assets:precompile && \ + rm -rf node_modules + +##### FINAL + +FROM ruby:3.4.1-alpine AS app + +ENV RAILS_ENV="production" \ + BUNDLE_PATH="/usr/local/bundle" \ + BUNDLE_WITHOUT="development test" + +WORKDIR /work + +RUN apk add -U --no-cache bash freetds + +COPY --from=base /usr/local/bundle /usr/local/bundle +COPY --from=base /work/ /work/ + +CMD ["/work/bin/start.sh"] diff --git a/Gemfile b/Gemfile index 53b0dbf..63481f5 100644 --- a/Gemfile +++ b/Gemfile @@ -19,12 +19,13 @@ gem "cssbundling-rails" gem "bcrypt", "~> 3.1.7" # Windows does not include zoneinfo files, so bundle the tzinfo-data gem -gem "tzinfo-data", platforms: %i[ windows jruby ] +gem "tzinfo-data" # Use the database-backed adapters for Rails.cache, Active Job, and Action Cable -gem "solid_cache" -gem "solid_queue" -gem "solid_cable" +# gem "solid_cache" +# gem "solid_queue" +# gem "solid_cable" + # Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images] # gem "image_processing", "~> 1.2" diff --git a/Gemfile.lock b/Gemfile.lock index 4a8d300..cd13a91 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -113,16 +113,11 @@ GEM drb (2.2.1) erubi (1.13.0) erubis (2.7.0) - et-orbi (1.2.11) - tzinfo factory_bot (6.5.0) activesupport (>= 5.0.0) factory_bot_rails (6.4.3) factory_bot (~> 6.4) railties (>= 5.0.0) - fugit (1.11.1) - et-orbi (~> 1, >= 1.2.11) - raabro (~> 1.4) globalid (1.2.1) activesupport (>= 6.1) haml (6.3.0) @@ -181,6 +176,8 @@ GEM racc (~> 1.4) nokogiri (1.18.1-x86_64-linux-gnu) racc (~> 1.4) + nokogiri (1.18.1-x86_64-linux-musl) + racc (~> 1.4) parallel (1.26.3) parser (3.3.5.0) ast (~> 2.4.1) @@ -198,7 +195,6 @@ GEM public_suffix (6.0.1) puma (6.4.3) nio4r (~> 2.0) - raabro (1.4.0) racc (1.8.1) rack (3.1.8) rack-session (2.0.0) @@ -278,22 +274,6 @@ GEM rubyzip (3.0.0.alpha) securerandom (0.3.2) sexp_processor (4.17.2) - solid_cable (3.0.2) - actioncable (>= 7.2) - activejob (>= 7.2) - activerecord (>= 7.2) - railties (>= 7.2) - solid_cache (1.0.6) - activejob (>= 7.2) - activerecord (>= 7.2) - railties (>= 7.2) - solid_queue (1.0.0) - activejob (>= 7.1) - activerecord (>= 7.1) - concurrent-ruby (>= 1.3.1) - fugit (~> 1.11.0) - railties (>= 7.1) - thor (~> 1.3.1) stimulus-rails (1.3.4) railties (>= 6.0.0) stringio (3.1.2) @@ -308,6 +288,8 @@ GEM railties (>= 6.0.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) + tzinfo-data (1.2024.2) + tzinfo (>= 1.0.0) unicode-display_width (2.6.0) uri (1.0.1) useragent (0.16.10) @@ -327,6 +309,8 @@ GEM PLATFORMS ruby x86_64-linux + x86_64-linux-gnu + x86_64-linux-musl DEPENDENCIES activerecord-sqlserver-adapter @@ -346,9 +330,6 @@ DEPENDENCIES rails (~> 8.0) rubocop-rails-omakase rubyzip (= 3.0.0.alpha) - solid_cable - solid_cache - solid_queue stimulus-rails turbo-rails tzinfo-data diff --git a/app/controllers/api/v1/binaries_controller.rb b/app/controllers/api/v1/binaries_controller.rb index 6eb0c07..43fad35 100644 --- a/app/controllers/api/v1/binaries_controller.rb +++ b/app/controllers/api/v1/binaries_controller.rb @@ -16,4 +16,4 @@ def download end end end -end \ No newline at end of file +end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb new file mode 100644 index 0000000..3e9026a --- /dev/null +++ b/app/mailers/application_mailer.rb @@ -0,0 +1,4 @@ +class ApplicationMailer < ActionMailer::Base + default from: "from@example.com" + layout "mailer" +end diff --git a/bin/start.sh b/bin/start.sh new file mode 100755 index 0000000..c483a2d --- /dev/null +++ b/bin/start.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +set -eux + +# so SQL server once tells its up only to go down again +# so we wait a little bit and wait again +./bin/wait-for-it.sh sql:1433 +sleep 5 +./bin/wait-for-it.sh sql:1433 + +bundle exec rails db:migrate || bundle exec rails db:setup + +bundle exec puma diff --git a/bin/wait-for-it.sh b/bin/wait-for-it.sh new file mode 100755 index 0000000..d990e0d --- /dev/null +++ b/bin/wait-for-it.sh @@ -0,0 +1,182 @@ +#!/usr/bin/env bash +# Use this script to test if a given TCP host/port are available + +WAITFORIT_cmdname=${0##*/} + +echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi } + +usage() +{ + cat << USAGE >&2 +Usage: + $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args] + -h HOST | --host=HOST Host or IP under test + -p PORT | --port=PORT TCP port under test + Alternatively, you specify the host and port as host:port + -s | --strict Only execute subcommand if the test succeeds + -q | --quiet Don't output any status messages + -t TIMEOUT | --timeout=TIMEOUT + Timeout in seconds, zero for no timeout + -- COMMAND ARGS Execute command with args after the test finishes +USAGE + exit 1 +} + +wait_for() +{ + if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then + echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" + else + echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout" + fi + WAITFORIT_start_ts=$(date +%s) + while : + do + if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then + nc -z $WAITFORIT_HOST $WAITFORIT_PORT + WAITFORIT_result=$? + else + (echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1 + WAITFORIT_result=$? + fi + if [[ $WAITFORIT_result -eq 0 ]]; then + WAITFORIT_end_ts=$(date +%s) + echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds" + break + fi + sleep 1 + done + return $WAITFORIT_result +} + +wait_for_wrapper() +{ + # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 + if [[ $WAITFORIT_QUIET -eq 1 ]]; then + timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & + else + timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & + fi + WAITFORIT_PID=$! + trap "kill -INT -$WAITFORIT_PID" INT + wait $WAITFORIT_PID + WAITFORIT_RESULT=$? + if [[ $WAITFORIT_RESULT -ne 0 ]]; then + echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" + fi + return $WAITFORIT_RESULT +} + +# process arguments +while [[ $# -gt 0 ]] +do + case "$1" in + *:* ) + WAITFORIT_hostport=(${1//:/ }) + WAITFORIT_HOST=${WAITFORIT_hostport[0]} + WAITFORIT_PORT=${WAITFORIT_hostport[1]} + shift 1 + ;; + --child) + WAITFORIT_CHILD=1 + shift 1 + ;; + -q | --quiet) + WAITFORIT_QUIET=1 + shift 1 + ;; + -s | --strict) + WAITFORIT_STRICT=1 + shift 1 + ;; + -h) + WAITFORIT_HOST="$2" + if [[ $WAITFORIT_HOST == "" ]]; then break; fi + shift 2 + ;; + --host=*) + WAITFORIT_HOST="${1#*=}" + shift 1 + ;; + -p) + WAITFORIT_PORT="$2" + if [[ $WAITFORIT_PORT == "" ]]; then break; fi + shift 2 + ;; + --port=*) + WAITFORIT_PORT="${1#*=}" + shift 1 + ;; + -t) + WAITFORIT_TIMEOUT="$2" + if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi + shift 2 + ;; + --timeout=*) + WAITFORIT_TIMEOUT="${1#*=}" + shift 1 + ;; + --) + shift + WAITFORIT_CLI=("$@") + break + ;; + --help) + usage + ;; + *) + echoerr "Unknown argument: $1" + usage + ;; + esac +done + +if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then + echoerr "Error: you need to provide a host and port to test." + usage +fi + +WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15} +WAITFORIT_STRICT=${WAITFORIT_STRICT:-0} +WAITFORIT_CHILD=${WAITFORIT_CHILD:-0} +WAITFORIT_QUIET=${WAITFORIT_QUIET:-0} + +# Check to see if timeout is from busybox? +WAITFORIT_TIMEOUT_PATH=$(type -p timeout) +WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH) + +WAITFORIT_BUSYTIMEFLAG="" +if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then + WAITFORIT_ISBUSY=1 + # Check if busybox timeout uses -t flag + # (recent Alpine versions don't support -t anymore) + if timeout &>/dev/stdout | grep -q -e '-t '; then + WAITFORIT_BUSYTIMEFLAG="-t" + fi +else + WAITFORIT_ISBUSY=0 +fi + +if [[ $WAITFORIT_CHILD -gt 0 ]]; then + wait_for + WAITFORIT_RESULT=$? + exit $WAITFORIT_RESULT +else + if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then + wait_for_wrapper + WAITFORIT_RESULT=$? + else + wait_for + WAITFORIT_RESULT=$? + fi +fi + +if [[ $WAITFORIT_CLI != "" ]]; then + if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then + echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess" + exit $WAITFORIT_RESULT + fi + exec "${WAITFORIT_CLI[@]}" +else + exit $WAITFORIT_RESULT +fi diff --git a/config/application.rb b/config/application.rb index 6478120..eca239e 100644 --- a/config/application.rb +++ b/config/application.rb @@ -7,7 +7,7 @@ require "active_record/railtie" require "active_storage/engine" require "action_controller/railtie" -# require "action_mailer/railtie" +require "action_mailer/railtie" # require "action_mailbox/engine" # require "action_text/engine" require "action_view/railtie" diff --git a/config/cable.yml b/config/cable.yml index b9adc5a..53232e7 100644 --- a/config/cable.yml +++ b/config/cable.yml @@ -9,9 +9,4 @@ test: adapter: test production: - adapter: solid_cable - connects_to: - database: - writing: cable - polling_interval: 0.1.seconds - message_retention: 1.day + adapter: async diff --git a/config/database.yml b/config/database.yml index d5fb885..39098f5 100644 --- a/config/database.yml +++ b/config/database.yml @@ -12,3 +12,8 @@ development: test: <<: *default database: zip_uploader_test + +production: + <<: *default + database: zip_uploader_production + host: sql diff --git a/config/environments/production.rb b/config/environments/production.rb index ac6553c..490df33 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -25,10 +25,10 @@ config.active_storage.service = :local # Assume all access to the app is happening through a SSL-terminating reverse proxy. - config.assume_ssl = true + config.assume_ssl = false # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. - config.force_ssl = true + config.force_ssl = false # Skip http-to-https redirect for the default health check endpoint. # config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } } @@ -47,11 +47,11 @@ config.active_support.report_deprecations = false # Replace the default in-process memory cache store with a durable alternative. - config.cache_store = :solid_cache_store + # config.cache_store = :solid_cache_store # 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 } } + config.active_job.queue_adapter = :async + # config.solid_queue.connects_to = { database: { writing: :queue } } # Enable locale fallbacks for I18n (makes lookups for any locale fall back to diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..3090541 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,11 @@ +services: + app: + build: "." + ports: + - "3000:3000" + + sql: + image: "mcr.microsoft.com/mssql/server:2022-latest" + environment: + ACCEPT_EULA: "Y" + MSSQL_SA_PASSWORD: "c0MplicatedP@ssword" diff --git a/test/controllers/api/v1/binaries_controller_test.rb b/test/controllers/api/v1/binaries_controller_test.rb index 535a5a4..86af0fb 100644 --- a/test/controllers/api/v1/binaries_controller_test.rb +++ b/test/controllers/api/v1/binaries_controller_test.rb @@ -15,4 +15,4 @@ class BinariesControllerTest < ActionDispatch::IntegrationTest end end end -end \ No newline at end of file +end diff --git a/test/factories/binaries.rb b/test/factories/binaries.rb index fa4b6eb..dfed4e1 100644 --- a/test/factories/binaries.rb +++ b/test/factories/binaries.rb @@ -1,6 +1,6 @@ FactoryBot.define do factory :binary do - folder { build(:folder, :root_folder)} + folder { build(:folder, :root_folder) } file { file_fixture_upload("lorem_ipsum_1.jpg", "image/jpeg") } name { "lorem_ipsum_1.jpg" } end