diff --git a/.app_version b/.app_version index 78cfa5eb..21574090 100644 --- a/.app_version +++ b/.app_version @@ -1 +1 @@ -0.21.6 +0.22.0 diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 39a87c65..a2077946 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -2,7 +2,7 @@ FROM ruby:3.3.4-alpine ENV APP_PATH=/var/app -ENV BUNDLE_VERSION=2.5.9 +ENV BUNDLE_VERSION=2.5.21 ENV BUNDLE_PATH=/usr/local/bundle/gems ENV TMP_PATH=/tmp/ ENV RAILS_LOG_TO_STDOUT=true @@ -27,7 +27,7 @@ RUN apk -U add --no-cache \ && rm -rf /var/cache/apk/* \ && mkdir -p $APP_PATH -RUN gem update --system 3.5.7 && gem install bundler --version "$BUNDLE_VERSION" \ +RUN gem update --system 3.6.2 && gem install bundler --version "$BUNDLE_VERSION" \ && rm -rf $GEM_HOME/cache/* # FIXME It would be a good idea to use a other user than root, but this lead to permission error on export and maybe more yet. diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index e0bc7867..56fb8d5f 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -35,7 +35,7 @@ services: PROMETHEUS_EXPORTER_PORT: 9394 ENABLE_TELEMETRY: false # More on telemetry: https://dawarich.app/docs/tutorials/telemetry dawarich_redis: - image: redis:7.0-alpine + image: redis:7.4-alpine container_name: dawarich_redis command: redis-server networks: @@ -50,7 +50,7 @@ services: start_period: 30s timeout: 10s dawarich_db: - image: postgres:14.2-alpine + image: postgres:17-alpine container_name: dawarich_db volumes: - dawarich_db_data:/var/lib/postgresql/data diff --git a/.github/workflows/build_and_push.yml b/.github/workflows/build_and_push.yml index 2c1ebe4c..00a78a71 100644 --- a/.github/workflows/build_and_push.yml +++ b/.github/workflows/build_and_push.yml @@ -40,7 +40,7 @@ jobs: uses: docker/build-push-action@v2 with: context: . - file: ./Dockerfile + file: ./docker/Dockerfile.dev push: true tags: freikin/dawarich:latest,freikin/dawarich:${{ github.event.inputs.branch || github.ref_name }} platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b2e1169..09c51769 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,34 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +# 0.22.0 - 2025-01-09 + +⚠️ This release introduces a breaking change. ⚠️ + +Please read this release notes carefully before upgrading. + +Docker-related files were moved to the `docker` directory and some of them were renamed. Before upgrading, study carefully changes in the `docker/docker-compose.yml` file and update your docker-compose file accordingly, so it uses the new files and commands. Copying `docker/docker-compose.yml` blindly may lead to errors. + +No volumes were removed or renamed, so with a proper docker-compose file, you should be able to upgrade without any issues. To make it easier comparing your existing docker-compose file with the new one, you may use https://www.diffchecker.com/. + +Although `docker-compose.production.yml` was added, it's not being used by default. It's just an example of how to configure Dawarich for production. The default `docker-compose.yml` file is still recommended for running the app. + +### Changed + +- All docker-related files were moved to the `docker` directory. +- Default memory limit for `dawarich_app` and `dawarich_sidekiq` services was increased to 4GB. +- `dawarich_app` and `dawarich_sidekiq` services now use separate entrypoint scripts. +- Gems (dependency libraries) are now being shipped as part of the Dawarich Docker image. + +### Fixed + +- Visit suggesting job does nothing if user has no tracked points. +- `BulkStatsCalculationJob` now being called without arguments in the data migration. + +### Added + +- A proper production Dockerfile, docker-compose and env files. + # 0.21.6 - 2025-01-07 ### Changed diff --git a/Dockerfile.dev b/Dockerfile.dev deleted file mode 100644 index 8d668cf3..00000000 --- a/Dockerfile.dev +++ /dev/null @@ -1,41 +0,0 @@ -FROM ruby:3.3.4-alpine - -ENV APP_PATH=/var/app -ENV BUNDLE_VERSION=2.5.9 -ENV BUNDLE_PATH=/usr/local/bundle/gems -ENV TMP_PATH=/tmp/ -ENV RAILS_LOG_TO_STDOUT=true -ENV RAILS_PORT=3000 - -# install dependencies for application -RUN apk -U add --no-cache \ - build-base \ - git \ - postgresql-dev \ - postgresql-client \ - libxml2-dev \ - libxslt-dev \ - nodejs \ - yarn \ - imagemagick \ - tzdata \ - less \ - # gcompat for nokogiri on mac m1 - gcompat \ - && rm -rf /var/cache/apk/* \ - && mkdir -p $APP_PATH - -RUN gem install bundler --version "$BUNDLE_VERSION" \ - && rm -rf $GEM_HOME/cache/* - -# copy entrypoint scripts and grant execution permissions -COPY ./dev-docker-entrypoint.sh /usr/local/bin/dev-entrypoint.sh -COPY ./test-docker-entrypoint.sh /usr/local/bin/test-entrypoint.sh -RUN chmod +x /usr/local/bin/dev-entrypoint.sh && chmod +x /usr/local/bin/test-entrypoint.sh - -# navigate to app directory -WORKDIR $APP_PATH - -EXPOSE $RAILS_PORT - -ENTRYPOINT [ "bundle", "exec" ] diff --git a/app/controllers/api/v1/health_controller.rb b/app/controllers/api/v1/health_controller.rb index 53563cb0..62e594e9 100644 --- a/app/controllers/api/v1/health_controller.rb +++ b/app/controllers/api/v1/health_controller.rb @@ -8,4 +8,3 @@ def index render json: { status: 'ok' } end end - diff --git a/app/jobs/visit_suggesting_job.rb b/app/jobs/visit_suggesting_job.rb index 06883b64..b1a3e13d 100644 --- a/app/jobs/visit_suggesting_job.rb +++ b/app/jobs/visit_suggesting_job.rb @@ -7,6 +7,10 @@ class VisitSuggestingJob < ApplicationJob def perform(user_ids: [], start_at: 1.day.ago, end_at: Time.current) users = user_ids.any? ? User.where(id: user_ids) : User.all - users.find_each { Visits::Suggest.new(_1, start_at:, end_at:).call } + users.find_each do |user| + next if user.tracked_points.empty? + + Visits::Suggest.new(user, start_at:, end_at:).call + end end end diff --git a/config/database.yml b/config/database.yml index 1977b027..fca7a51c 100644 --- a/config/database.yml +++ b/config/database.yml @@ -20,4 +20,3 @@ test: production: <<: *default database: <%= ENV['DATABASE_NAME'] || 'dawarich_production' %> - url: <%= ENV['DATABASE_URL'] %> diff --git a/config/environments/production.rb b/config/environments/production.rb index 8b1a2d2a..4e8f5661 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -29,7 +29,11 @@ # config.assets.css_compressor = :sass # Do not fallback to assets pipeline if a precompiled asset is missed. - config.assets.compile = false + config.assets.compile = true + + config.assets.content_type = { + geojson: 'application/geo+json' + } # Enable serving of images, stylesheets, and JavaScripts from an asset server. # config.asset_host = "http://assets.example.com" @@ -53,12 +57,12 @@ # 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 = ENV.fetch('APPLICATION_PROTOCOL', 'http').downcase == 'https' - # Log to STDOUT by default - config.logger = ActiveSupport::Logger.new($stdout) - .tap { |logger| logger.formatter = ::Logger::Formatter.new } - .then { |logger| ActiveSupport::TaggedLogging.new(logger) } + # Direct logs to STDOUT + config.logger = Logger.new($stdout) + config.lograge.enabled = true + config.lograge.formatter = Lograge::Formatters::Json.new # Prepend all log lines with the following tags. config.log_tags = [:request_id] @@ -99,4 +103,8 @@ # ] # Skip DNS rebinding protection for the default health check endpoint. # config.host_authorization = { exclude: ->(request) { request.path == "/up" } } + hosts = ENV.fetch('APPLICATION_HOSTS', 'localhost').split(',') + + config.action_mailer.default_url_options = { host: hosts.first, port: 3000 } + config.hosts.concat(hosts) if hosts.present? end diff --git a/db/data/20240610170930_remove_points_without_coordinates.rb b/db/data/20240610170930_remove_points_without_coordinates.rb index b8647672..85ef7684 100644 --- a/db/data/20240610170930_remove_points_without_coordinates.rb +++ b/db/data/20240610170930_remove_points_without_coordinates.rb @@ -12,7 +12,7 @@ def up Rails.logger.info 'Points without coordinates removed.' - BulkStatsCalculatingJob.perform_later(User.pluck(:id)) + BulkStatsCalculatingJob.perform_later end def down diff --git a/dev-docker-entrypoint.sh b/dev-docker-entrypoint.sh deleted file mode 100644 index 385b50da..00000000 --- a/dev-docker-entrypoint.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/sh - -unset BUNDLE_PATH -unset BUNDLE_BIN - -set -e - -echo "Environment: $RAILS_ENV" - -# set env var defaults -DATABASE_HOST=${DATABASE_HOST:-"dawarich_db"} -DATABASE_PORT=${DATABASE_PORT:-5432} -DATABASE_USERNAME=${DATABASE_USERNAME:-"postgres"} -DATABASE_PASSWORD=${DATABASE_PASSWORD:-"password"} -DATABASE_NAME=${DATABASE_NAME:-"dawarich_development"} - -# Remove pre-existing puma/passenger server.pid -rm -f $APP_PATH/tmp/pids/server.pid - -# Wait for the database to be ready -until nc -zv $DATABASE_HOST ${DATABASE_PORT:-5432}; do - echo "Waiting for PostgreSQL to be ready..." - sleep 1 -done - -# Install gems -gem update --system 3.5.7 -gem install bundler --version '2.5.9' - -# Create the database -if [ "$(psql "postgres://$DATABASE_USERNAME:$DATABASE_PASSWORD@$DATABASE_HOST:$DATABASE_PORT" -XtAc "SELECT 1 FROM pg_database WHERE datname='$DATABASE_NAME'")" = '1' ]; then - echo "Database $DATABASE_NAME already exists, skipping creation..." -else - echo "Creating database $DATABASE_NAME..." - bundle exec rails db:create -fi - -# Run database migrations -echo "PostgreSQL is ready. Running database migrations..." -bundle exec rails db:prepare - -# Run data migrations -echo "Running DATA migrations..." -bundle exec rake data:migrate - -# Run seeds -echo "Running seeds..." -bundle exec rake db:seed - -# run passed commands -bundle exec ${@} diff --git a/dev-docker-sidekiq-entrypoint.sh b/dev-docker-sidekiq-entrypoint.sh deleted file mode 100644 index db7c87e9..00000000 --- a/dev-docker-sidekiq-entrypoint.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh - -set -e - -echo "Environment: $RAILS_ENV" - -# set env var defaults -DATABASE_HOST=${DATABASE_HOST:-"dawarich_db"} -DATABASE_PORT=${DATABASE_PORT:-5432} -DATABASE_USER=${DATABASE_USER:-"postgres"} -DATABASE_PASSWORD=${DATABASE_PASSWORD:-"password"} -DATABASE_NAME=${DATABASE_NAME:-"dawarich_development"} - -# Wait for the database to be ready -until nc -zv $DATABASE_HOST ${DATABASE_PORT:-5432}; do - echo "Waiting for PostgreSQL to be ready..." - sleep 1 -done - -# run passed commands -bundle exec ${@} diff --git a/.dockerignore b/docker/.dockerignore similarity index 100% rename from .dockerignore rename to docker/.dockerignore diff --git a/Dockerfile b/docker/Dockerfile.dev similarity index 54% rename from Dockerfile rename to docker/Dockerfile.dev index d809b2d7..381ece15 100644 --- a/Dockerfile +++ b/docker/Dockerfile.dev @@ -1,9 +1,8 @@ FROM ruby:3.3.4-alpine ENV APP_PATH=/var/app -ENV BUNDLE_VERSION=2.5.9 +ENV BUNDLE_VERSION=2.5.21 ENV BUNDLE_PATH=/usr/local/bundle/gems -ENV TMP_PATH=/tmp/ ENV RAILS_LOG_TO_STDOUT=true ENV RAILS_PORT=3000 @@ -21,28 +20,30 @@ RUN apk -U add --no-cache \ tzdata \ less \ yaml-dev \ - # gcompat for nokogiri on mac m1 gcompat \ - && rm -rf /var/cache/apk/* \ && mkdir -p $APP_PATH -RUN gem install bundler --version "$BUNDLE_VERSION" \ +# Update gem system and install bundler +RUN gem update --system 3.6.2 \ + && gem install bundler --version "$BUNDLE_VERSION" \ && rm -rf $GEM_HOME/cache/* -# Navigate to app directory WORKDIR $APP_PATH -COPY Gemfile Gemfile.lock vendor .ruby-version ./ +COPY ../Gemfile ../Gemfile.lock ../vendor ../.ruby-version ./ -# Install missing gems +# Install all gems including development and test RUN bundle config set --local path 'vendor/bundle' \ - && bundle install --jobs 20 --retry 5 + && bundle install --jobs 4 --retry 3 -COPY . ./ +COPY ../. ./ # Copy entrypoint scripts and grant execution permissions -COPY ./dev-docker-entrypoint.sh /usr/local/bin/dev-entrypoint.sh -RUN chmod +x /usr/local/bin/dev-entrypoint.sh +COPY ./docker/web-entrypoint.sh /usr/local/bin/web-entrypoint.sh +RUN chmod +x /usr/local/bin/web-entrypoint.sh + +COPY ./docker/sidekiq-entrypoint.sh /usr/local/bin/sidekiq-entrypoint.sh +RUN chmod +x /usr/local/bin/sidekiq-entrypoint.sh EXPOSE $RAILS_PORT diff --git a/docker/Dockerfile.prod b/docker/Dockerfile.prod new file mode 100644 index 00000000..a8f6a449 --- /dev/null +++ b/docker/Dockerfile.prod @@ -0,0 +1,55 @@ +FROM ruby:3.3.4-alpine + +ENV APP_PATH=/var/app +ENV BUNDLE_VERSION=2.5.21 +ENV BUNDLE_PATH=/usr/local/bundle/gems +ENV RAILS_LOG_TO_STDOUT=true +ENV RAILS_PORT=3000 +ENV RAILS_ENV=production + +# Install dependencies for application +RUN apk -U add --no-cache \ + build-base \ + git \ + postgresql-dev \ + postgresql-client \ + libxml2-dev \ + libxslt-dev \ + nodejs \ + yarn \ + imagemagick \ + tzdata \ + less \ + yaml-dev \ + gcompat \ + && mkdir -p $APP_PATH + +# Update gem system and install bundler +RUN gem update --system 3.6.2 \ + && gem install bundler --version "$BUNDLE_VERSION" \ + && rm -rf $GEM_HOME/cache/* + +WORKDIR $APP_PATH + +COPY ../Gemfile ../Gemfile.lock ../vendor ../.ruby-version ./ + +# Install production gems only +RUN bundle config set --local path 'vendor/bundle' \ + && bundle install --jobs 4 --retry 3 --without development test + +COPY ../. ./ + +# Precompile assets for production +RUN bundle exec rake assets:precompile \ + && rm -rf node_modules tmp/cache + +# Copy entrypoint scripts and grant execution permissions +COPY ./docker/web-entrypoint.sh /usr/local/bin/web-entrypoint.sh +RUN chmod +x /usr/local/bin/web-entrypoint.sh + +COPY ./docker/sidekiq-entrypoint.sh /usr/local/bin/sidekiq-entrypoint.sh +RUN chmod +x /usr/local/bin/sidekiq-entrypoint.sh + +EXPOSE $RAILS_PORT + +ENTRYPOINT [ "bundle", "exec" ] diff --git a/docker/docker-compose.production.yml b/docker/docker-compose.production.yml new file mode 100644 index 00000000..1eb90902 --- /dev/null +++ b/docker/docker-compose.production.yml @@ -0,0 +1,160 @@ +networks: + dawarich: +services: + dawarich_redis: + image: redis:7.4-alpine + container_name: dawarich_redis + command: redis-server + networks: + - dawarich + volumes: + - dawarich_redis_data:/var/shared/redis + restart: always + healthcheck: + test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ] + interval: 10s + retries: 5 + start_period: 30s + timeout: 10s + dawarich_db: + image: postgres:17-alpine + shm_size: 1G + container_name: dawarich_db + volumes: + - dawarich_db_data:/var/lib/postgresql/data + networks: + - dawarich + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: password + POSTGRES_DB: dawarich_production + restart: always + healthcheck: + test: [ "CMD", "pg_isready", "-U", "postgres" ] + interval: 10s + retries: 5 + start_period: 30s + timeout: 10s + dawarich_app: + image: freikin/dawarich:latest + container_name: dawarich_app + volumes: + - dawarich_gem_cache_app:/usr/local/bundle/gems + - dawarich_public:/var/app/public + - dawarich_watched:/var/app/tmp/imports/watched + networks: + - dawarich + ports: + - 3000:3000 + # - 9394:9394 # Prometheus exporter, uncomment if needed + stdin_open: true + tty: true + entrypoint: web-entrypoint.sh + command: ['bin/rails', 'server', '-p', '3000', '-b', '::'] + restart: on-failure + environment: + RAILS_ENV: production + REDIS_URL: redis://dawarich_redis:6379/0 + DATABASE_HOST: dawarich_db + DATABASE_PORT: 5432 + DATABASE_USERNAME: postgres + DATABASE_PASSWORD: password + DATABASE_NAME: dawarich_production + MIN_MINUTES_SPENT_IN_CITY: 60 + APPLICATION_HOSTS: localhost,::1,127.0.0.1 + TIME_ZONE: Europe/London + APPLICATION_PROTOCOL: http + DISTANCE_UNIT: km + PROMETHEUS_EXPORTER_ENABLED: false + PROMETHEUS_EXPORTER_HOST: 0.0.0.0 + PROMETHEUS_EXPORTER_PORT: 9394 + SECRET_KEY_BASE: 1234567890 + RAILS_LOG_TO_STDOUT: "true" + logging: + driver: "json-file" + options: + max-size: "100m" + max-file: "5" + healthcheck: + test: [ "CMD-SHELL", "wget -qO - http://127.0.0.1:3000/api/v1/health | grep -q '\"status\"\\s*:\\s*\"ok\"'" ] + interval: 10s + retries: 30 + start_period: 30s + timeout: 10s + depends_on: + dawarich_db: + condition: service_healthy + restart: true + dawarich_redis: + condition: service_healthy + restart: true + deploy: + resources: + limits: + cpus: '0.50' # Limit CPU usage to 50% of one core + memory: '2G' # Limit memory usage to 2GB + dawarich_sidekiq: + image: freikin/dawarich:latest + container_name: dawarich_sidekiq + volumes: + - dawarich_gem_cache_sidekiq:/usr/local/bundle/gems + - dawarich_public:/var/app/public + - dawarich_watched:/var/app/tmp/imports/watched + networks: + - dawarich + stdin_open: true + tty: true + entrypoint: sidekiq-entrypoint.sh + command: ['bundle', 'exec', 'sidekiq'] + restart: on-failure + environment: + RAILS_ENV: production + REDIS_URL: redis://dawarich_redis:6379/0 + DATABASE_HOST: dawarich_db + DATABASE_PORT: 5432 + DATABASE_USERNAME: postgres + DATABASE_PASSWORD: password + DATABASE_NAME: dawarich_production + APPLICATION_HOSTS: localhost,::1,127.0.0.1 + BACKGROUND_PROCESSING_CONCURRENCY: 10 + APPLICATION_PROTOCOL: http + DISTANCE_UNIT: km + PROMETHEUS_EXPORTER_ENABLED: false + PROMETHEUS_EXPORTER_HOST: dawarich_app + PROMETHEUS_EXPORTER_PORT: 9394 + SECRET_KEY_BASE: 1234567890 + RAILS_LOG_TO_STDOUT: "true" + logging: + driver: "json-file" + options: + max-size: "100m" + max-file: "5" + healthcheck: + test: [ "CMD-SHELL", "bundle exec sidekiqmon processes | grep $${HOSTNAME}" ] + interval: 10s + retries: 30 + start_period: 30s + timeout: 10s + depends_on: + dawarich_db: + condition: service_healthy + restart: true + dawarich_redis: + condition: service_healthy + restart: true + dawarich_app: + condition: service_healthy + restart: true + deploy: + resources: + limits: + cpus: '0.50' # Limit CPU usage to 50% of one core + memory: '2G' # Limit memory usage to 2GB + +volumes: + dawarich_db_data: + dawarich_redis_data: + dawarich_gem_cache_app: + dawarich_gem_cache_sidekiq: + dawarich_public: + dawarich_watched: diff --git a/docker-compose.yml b/docker/docker-compose.yml similarity index 94% rename from docker-compose.yml rename to docker/docker-compose.yml index 53586a39..d2154c2c 100644 --- a/docker-compose.yml +++ b/docker/docker-compose.yml @@ -51,8 +51,8 @@ services: # - 9394:9394 # Prometheus exporter, uncomment if needed stdin_open: true tty: true - entrypoint: dev-entrypoint.sh - command: ['bin/dev'] + entrypoint: web-entrypoint.sh + command: ['bin/rails', 'server', '-p', '3000', '-b', '::'] restart: on-failure environment: RAILS_ENV: development @@ -92,7 +92,7 @@ services: resources: limits: cpus: '0.50' # Limit CPU usage to 50% of one core - memory: '2G' # Limit memory usage to 2GB + memory: '4G' # Limit memory usage to 4GB dawarich_sidekiq: image: freikin/dawarich:latest container_name: dawarich_sidekiq @@ -104,7 +104,7 @@ services: - dawarich stdin_open: true tty: true - entrypoint: dev-entrypoint.sh + entrypoint: sidekiq-entrypoint.sh command: ['sidekiq'] restart: on-failure environment: @@ -147,7 +147,7 @@ services: resources: limits: cpus: '0.50' # Limit CPU usage to 50% of one core - memory: '2G' # Limit memory usage to 2GB + memory: '4G' # Limit memory usage to 4GB volumes: dawarich_db_data: diff --git a/docker-compose_mounted_volumes.yml b/docker/docker-compose_mounted_volumes.yml similarity index 100% rename from docker-compose_mounted_volumes.yml rename to docker/docker-compose_mounted_volumes.yml diff --git a/docker/sidekiq-entrypoint.sh b/docker/sidekiq-entrypoint.sh new file mode 100644 index 00000000..1083891b --- /dev/null +++ b/docker/sidekiq-entrypoint.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +unset BUNDLE_PATH +unset BUNDLE_BIN + +set -e + +echo "⚠️ Starting Sidekiq in $RAILS_ENV environment ⚠️" + +# Parse DATABASE_URL if present, otherwise use individual variables +if [ -n "$DATABASE_URL" ]; then + # Extract components from DATABASE_URL + DATABASE_HOST=$(echo $DATABASE_URL | awk -F[@/] '{print $4}') + DATABASE_PORT=$(echo $DATABASE_URL | awk -F[@/:] '{print $5}') + DATABASE_USERNAME=$(echo $DATABASE_URL | awk -F[:/@] '{print $4}') + DATABASE_PASSWORD=$(echo $DATABASE_URL | awk -F[:/@] '{print $5}') +else + # Use existing environment variables + DATABASE_HOST=${DATABASE_HOST} + DATABASE_PORT=${DATABASE_PORT} + DATABASE_USERNAME=${DATABASE_USERNAME} + DATABASE_PASSWORD=${DATABASE_PASSWORD} +fi + +# Wait for the database to become available +echo "⏳ Waiting for database to be ready..." +until PGPASSWORD=$DATABASE_PASSWORD psql -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USERNAME" -c '\q'; do + >&2 echo "Postgres is unavailable - retrying..." + sleep 2 +done +echo "✅ PostgreSQL is ready!" + +# run sidekiq +bundle exec sidekiq diff --git a/docker/web-entrypoint.sh b/docker/web-entrypoint.sh new file mode 100644 index 00000000..cfd52116 --- /dev/null +++ b/docker/web-entrypoint.sh @@ -0,0 +1,58 @@ +#!/bin/sh + +unset BUNDLE_PATH +unset BUNDLE_BIN + +set -e + +echo "⚠️ Starting Rails environment: $RAILS_ENV ⚠️" + +# Parse DATABASE_URL if present, otherwise use individual variables +if [ -n "$DATABASE_URL" ]; then + # Extract components from DATABASE_URL + DATABASE_HOST=$(echo $DATABASE_URL | awk -F[@/] '{print $4}') + DATABASE_PORT=$(echo $DATABASE_URL | awk -F[@/:] '{print $5}') + DATABASE_USERNAME=$(echo $DATABASE_URL | awk -F[:/@] '{print $4}') + DATABASE_PASSWORD=$(echo $DATABASE_URL | awk -F[:/@] '{print $5}') + DATABASE_NAME=$(echo $DATABASE_URL | awk -F[@/] '{print $5}') +else + # Use existing environment variables + DATABASE_HOST=${DATABASE_HOST} + DATABASE_PORT=${DATABASE_PORT} + DATABASE_USERNAME=${DATABASE_USERNAME} + DATABASE_PASSWORD=${DATABASE_PASSWORD} + DATABASE_NAME=${DATABASE_NAME} +fi + +# Remove pre-existing puma/passenger server.pid +rm -f $APP_PATH/tmp/pids/server.pid + +# Wait for the database to become available +echo "⏳ Waiting for database to be ready..." +until PGPASSWORD=$DATABASE_PASSWORD psql -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USERNAME" -c '\q'; do + >&2 echo "Postgres is unavailable - retrying..." + sleep 2 +done +echo "✅ PostgreSQL is ready!" + +# Create database if it doesn't exist +if ! PGPASSWORD=$DATABASE_PASSWORD psql -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USERNAME" -c "SELECT 1 FROM pg_database WHERE datname='$DATABASE_NAME'" | grep -q 1; then + echo "Creating database $DATABASE_NAME..." + bundle exec rails db:create +fi + +# Run database migrations +echo "PostgreSQL is ready. Running database migrations..." +bundle exec rails db:migrate + +# Run data migrations +echo "Running DATA migrations..." +bundle exec rake data:migrate + +if [ "$RAILS_ENV" != "production" ]; then + echo "Running seeds..." + bundle exec rails db:seed +fi + +# run passed commands +bundle exec ${@} diff --git a/spec/jobs/visit_suggesting_job_spec.rb b/spec/jobs/visit_suggesting_job_spec.rb index 70af4e74..f2ce47d9 100644 --- a/spec/jobs/visit_suggesting_job_spec.rb +++ b/spec/jobs/visit_suggesting_job_spec.rb @@ -3,9 +3,9 @@ require 'rails_helper' RSpec.describe VisitSuggestingJob, type: :job do - describe '#perform' do - let!(:users) { [create(:user)] } + let!(:users) { [create(:user)] } + describe '#perform' do subject { described_class.perform_now } before do @@ -13,10 +13,22 @@ allow_any_instance_of(Visits::Suggest).to receive(:call) end - it 'suggests visits' do - subject + context 'when user has no tracked points' do + it 'does not suggest visits' do + subject + + expect(Visits::Suggest).not_to have_received(:new) + end + end + + context 'when user has tracked points' do + let!(:tracked_point) { create(:point, user: users.first) } + + it 'suggests visits' do + subject - expect(Visits::Suggest).to have_received(:new) + expect(Visits::Suggest).to have_received(:new) + end end end end