diff --git a/.circleci/config.yml b/.circleci/config.yml index 7dd7c37a075..21728ea5eaf 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -32,6 +32,7 @@ executors: <<: *environment DB: mysql DB_HOST: 127.0.0.1 + DB_USERNAME: root docker: - image: *image - image: circleci/mysql:5.7-ram diff --git a/.dockerdev/.psqlrc b/.dockerdev/.psqlrc new file mode 100644 index 00000000000..6a0fa9dc35c --- /dev/null +++ b/.dockerdev/.psqlrc @@ -0,0 +1 @@ +\set HISTFILE ~/history/psql_history diff --git a/.dockerdev/Dockerfile b/.dockerdev/Dockerfile new file mode 100644 index 00000000000..fc6e9732544 --- /dev/null +++ b/.dockerdev/Dockerfile @@ -0,0 +1,57 @@ +ARG RUBY_VERSION +FROM ruby:$RUBY_VERSION-slim-buster + +ARG PG_VERSION +ARG MYSQL_VERSION +ARG NODE_VERSION +ARG BUNDLER_VERSION + +RUN apt-get update -qq \ + && DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \ + build-essential \ + gnupg2 \ + curl \ + git \ + imagemagick \ + libmariadb-dev \ + sqlite3 \ + libsqlite3-dev \ + chromium \ + chromium-driver \ + && rm -rf /var/cache/apt/lists/* + +RUN curl -sSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - \ + && echo 'deb http://apt.postgresql.org/pub/repos/apt/ buster-pgdg main' $PG_VERSION > /etc/apt/sources.list.d/pgdg.list + +RUN apt-key adv --keyserver ha.pool.sks-keyservers.net --recv-keys 8C718D3B5072E1F5 \ + && echo "deb http://repo.mysql.com/apt/debian/ buster mysql-"$MYSQL_VERSION > /etc/apt/sources.list.d/mysql.list + +RUN curl -sSL https://deb.nodesource.com/setup_$NODE_VERSION.x | bash - + +RUN apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get -yq dist-upgrade && \ + DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \ + libpq-dev \ + postgresql-client-$PG_VERSION \ + mysql-client \ + nodejs \ + && rm -rf /var/lib/apt/lists/* + +ENV APP_USER=solidus_user \ + LANG=C.UTF-8 \ + BUNDLE_JOBS=4 \ + BUNDLE_RETRY=3 +ENV GEM_HOME=/home/$APP_USER/gems +ENV APP_HOME=/home/$APP_USER/app +ENV PATH=$PATH:$GEM_HOME/bin + +RUN useradd -ms /bin/bash $APP_USER + +RUN gem update --system \ + && gem install bundler:$BUNDLER_VERSION \ + && chown -R $APP_USER:$(id -g $APP_USER) /home/$APP_USER/gems + +USER $APP_USER + +RUN mkdir -p /home/$APP_USER/history + +WORKDIR /home/$APP_USER/app diff --git a/.gitignore b/.gitignore index fbd33c3ad7a..6ddd5bb46d6 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ tags node_modules yarn.lock package-lock.json +.env diff --git a/Gemfile b/Gemfile index 90f367fca14..8a7f3a912e9 100644 --- a/Gemfile +++ b/Gemfile @@ -19,12 +19,13 @@ group :backend, :frontend, :core, :api do gem 'sprockets', '~> 3' platforms :ruby do - case ENV['DB'] - when /mysql/ + if /mysql/.match?(ENV['DB']) || ENV['DB_ALL'] gem 'mysql2', '~> 0.5.0', require: false - when /postgres/ + end + if /postgres/.match?(ENV['DB']) || ENV['DB_ALL'] gem 'pg', '~> 1.0', require: false - else + end + if ENV['DB_ALL'] || !/mysql|postgres/.match?(ENV['DB']) gem 'sqlite3', require: false gem 'fast_sqlite', require: false end diff --git a/README.md b/README.md index 4ffaa4d0311..6d2ede5c28e 100644 --- a/README.md +++ b/README.md @@ -238,6 +238,8 @@ and/or customizations to the Solidus admin. Use at your own risk. cd solidus ``` +### Without Docker + * Install the gem dependencies ```bash @@ -256,6 +258,60 @@ and/or customizations to the Solidus admin. Use at your own risk. bin/setup ``` +### With Docker + +```bash +docker-compose up -d +``` + +Wait for all the gems to be installed (progress can be checked through `docker-compose logs -f app`). + +You can provide the ruby version you want your image to use: + +```bash +docker-compose build --build-arg RUBY_VERSION=2.6 app +docker-compose up -d +``` + +The rails version can be customized at runtime through `RAILS_VERSION` environment variable: + +```bash +RAILS_VERSION='~> 5.0' docker-compose up -d +``` + +Running tests: + +```bash +# sqlite +docker-compose exec app bin/rspec +# postgres +docker-compose exec app env DB=postgres bin/rspec +# mysql +docker-compose exec app env DB=mysql bin/rspec +``` + +Accessing the databases: + +```bash +# sqlite +docker-compose exec app sqlite3 /path/to/db +# postgres +docker-compose exec app env PGPASSWORD=password psql -U root -h postgres +# mysql +docker-compose exec app mysql -u root -h mysql -ppassword +``` + +In order to be able to access the [sandbox application](#sandbox), just make +sure to provide the appropriate `--binding` option to `rails server`. By +default, port `3000` is exposed, but you can change it through `SANDBOX_PORT` +environment variable: + +```bash +SANDBOX_PORT=4000 docker-compose up -d +docker-compose exec app bin/sandbox +docker-compose exec app bin/rails server --binding 0.0.0.0 --port 4000 +``` + ### Sandbox Solidus is meant to be run within the context of Rails application. You can diff --git a/backend/spec/spec_helper.rb b/backend/spec/spec_helper.rb index b6707569806..d0d7b706e77 100644 --- a/backend/spec/spec_helper.rb +++ b/backend/spec/spec_helper.rb @@ -54,6 +54,15 @@ browser_options.args << '--window-size=1920,1080' Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options) end +Capybara.register_driver :selenium_chrome_headless_docker_friendly do |app| + browser_options = ::Selenium::WebDriver::Chrome::Options.new + browser_options.args << '--headless' + browser_options.args << '--disable-gpu' + # Sandbox cannot be used inside unprivileged Docker container + browser_options.args << '--no-sandbox' + browser_options.args << '--window-size=1240,1400' + Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options) +end Capybara.javascript_driver = (ENV['CAPYBARA_DRIVER'] || :selenium_chrome_headless).to_sym diff --git a/bin/sandbox b/bin/sandbox index b11dd93d092..3c2620492aa 100755 --- a/bin/sandbox +++ b/bin/sandbox @@ -7,9 +7,15 @@ set -e case "$DB" in postgres|postgresql) RAILSDB="postgresql" + HOST=${DB_POSTGRES_HOST:-${DB_HOST}} + USERNAME=$DB_USERNAME + PASSWORD=$DB_PASSWORD ;; mysql) RAILSDB="mysql" + HOST=${DB_MYSQL_HOST:-${DB_HOST}} + USERNAME=$DB_USERNAME + PASSWORD=$DB_PASSWORD ;; sqlite|'') RAILSDB="sqlite3" @@ -59,6 +65,27 @@ group :test, :development do end RUBY +replace_in_database_yml() { + if [ $RAILSDB = "postgresql" ]; then + sed -i.bck "/^ adapter:/a \ \ $1: $2" config/database.yml + elif [ $RAILSDB = "mysql" ]; then + sed -i.bck "s/^ $1:.*/\ \ $1: $2/" config/database.yml + fi + if [ -f config/database.yml.bck ]; then + rm -f config/database.yml.bck + fi +} + +if [ ${HOST} ]; then + replace_in_database_yml "host" $HOST +fi +if [ ${USERNAME} ]; then + replace_in_database_yml "username" $USERNAME +fi +if [ ${PASSWORD} ]; then + replace_in_database_yml "password" $PASSWORD +fi + unbundled bundle install --gemfile Gemfile unbundled bin/rails db:drop db:create unbundled bin/rails generate solidus:install \ diff --git a/core/lib/generators/spree/dummy/templates/rails/database.yml b/core/lib/generators/spree/dummy/templates/rails/database.yml index b4888a95600..8378ed815cb 100644 --- a/core/lib/generators/spree/dummy/templates/rails/database.yml +++ b/core/lib/generators/spree/dummy/templates/rails/database.yml @@ -1,66 +1,111 @@ <% if agent_number = ENV['TC_AGENT_NUMBER'] database_prefix = agent_number + '_' end %> +<% db = case ENV['DB'] + when 'mysql' + 'mysql' + when 'postgres', 'postgresql' + 'postgres' + when 'sqlite', '', nil + 'sqlite' + else + raise "Invalid DB specified: #{ENV['DB']}" + end %> +<% db_host = case db + when 'mysql' + ENV['DB_MYSQL_HOST'] || ENV['DB_HOST'] + when 'postgres' + ENV['DB_POSTGRES_HOST'] || ENV['DB_HOST'] + else + ENV['DB_HOST'] + end %> +<% db_username = ENV['DB_USERNAME'] %> +<% db_password = ENV['DB_PASSWORD'] %> + + + + <% case ENV['DB'] - when 'sqlite' %> -development: - adapter: sqlite3 - database: db/solidus_development.sqlite3 -test: - adapter: sqlite3 - database: db/solidus_test.sqlite3 - timeout: 10000 -production: - adapter: sqlite3 - database: db/solidus_production.sqlite3 -<% when 'mysql' %> + when 'mysql' %> development: adapter: mysql2 database: <%= database_prefix %><%= options[:lib_name] %>_solidus_development + <% unless db_username.blank? %> + username: <%= db_username %> + <% end %> + <% unless db_password.blank? %> + password: <%= db_password %> + <% end %> + <% unless db_host.blank? %> + host: <%= db_host %> + <% end %> encoding: utf8 test: adapter: mysql2 - <% if ENV['TRAVIS'] %> - username: root - password: - <% end %> database: <%= database_prefix %><%= options[:lib_name] %>_solidus_test + <% unless db_username.blank? %> + username: <%= db_username %> + <% end %> + <% unless db_password.blank? %> + password: <%= db_password %> + <% end %> + <% unless db_host.blank? %> + host: <%= db_host %> + <% end %> encoding: utf8 production: adapter: mysql2 database: <%= database_prefix %><%= options[:lib_name] %>_solidus_production + <% unless db_username.blank? %> + username: <%= db_username %> + <% end %> + <% unless db_password.blank? %> + password: <%= db_password %> + <% end %> + <% unless db_host.blank? %> + host: <%= db_host %> + <% end %> encoding: utf8 <% when 'postgres', 'postgresql' %> -<% db_host = ENV['DB_HOST'] -%> -<% db_username = ENV['DB_USERNAME'] -%> -<% db_password = ENV['DB_PASSWORD'] -%> development: adapter: postgresql database: <%= database_prefix %><%= options[:lib_name] %>_solidus_development - username: postgres - min_messages: warning -<% unless db_host.blank? %> + <% unless db_username.blank? %> + username: <%= db_username %> + <% end %> + <% unless db_password.blank? %> + password: <%= db_password %> + <% end %> + <% unless db_host.blank? %> host: <%= db_host %> -<% end %> + <% end %> + min_messages: warning test: adapter: postgresql database: <%= database_prefix %><%= options[:lib_name] %>_solidus_test - username: <%= db_username || 'postgres' %> -<% unless db_password.blank? %> + <% unless db_username.blank? %> + username: <%= db_username %> + <% end %> + <% unless db_password.blank? %> password: <%= db_password %> -<% end %> - min_messages: warning -<% unless db_host.blank? %> + <% end %> + <% unless db_host.blank? %> host: <%= db_host %> -<% end %> + <% end %> + min_messages: warning production: adapter: postgresql database: <%= database_prefix %><%= options[:lib_name] %>_solidus_production - username: postgres - min_messages: warning -<% unless db_host.blank? %> + <% unless db_username.blank? %> + username: <%= db_username %> + <% end %> + <% unless db_password.blank? %> + password: <%= db_password %> + <% end %> + <% unless db_host.blank? %> host: <%= db_host %> -<% end %> + <% end %> + min_messages: warning <% when 'sqlite', '', nil %> development: adapter: sqlite3 @@ -71,6 +116,4 @@ test: production: adapter: sqlite3 database: db/solidus_production.sqlite3 -<% else %> - <% raise "Invalid DB specified: #{ENV['DB']}" %> <% end %> diff --git a/core/lib/spree/testing_support/dummy_app/database.yml b/core/lib/spree/testing_support/dummy_app/database.yml index d1460e0831a..46179359a68 100644 --- a/core/lib/spree/testing_support/dummy_app/database.yml +++ b/core/lib/spree/testing_support/dummy_app/database.yml @@ -1,34 +1,54 @@ -<% -database_prefix = ENV['LIB_NAME'].presence || "solidus" -%> -<% case ENV['DB'] +<% db = case ENV['DB'] + when 'mysql' + 'mysql' + when 'postgres', 'postgresql' + 'postgres' + when 'sqlite', '', nil + 'sqlite' + else + raise "Invalid DB specified: #{ENV['DB']}" + end %> +<% db_host = case db + when 'mysql' + ENV['DB_MYSQL_HOST'] || ENV['DB_HOST'] + when 'postgres' + ENV['DB_POSTGRES_HOST'] || ENV['DB_HOST'] + else + ENV['DB_HOST'] + end %> +<% db_prefix = ENV['LIB_NAME'].presence || "solidus" %> +<% db_username = ENV['DB_USERNAME'] %> +<% db_password = ENV['DB_PASSWORD'] %> +<% case db when 'mysql' %> test: adapter: mysql2 - <% if ENV['CI'] %> - username: root - password: + database: <%= db_prefix %>_test + <% unless db_username.blank? %> + username: <%= db_username %> <% end %> - database: <%= database_prefix %>_test - encoding: utf8 - <% unless ENV['DB_HOST'].blank? %> - host: <%= ENV['DB_HOST'] %> + <% unless db_password.blank? %> + password: <%= db_password %> + <% end %> + <% unless db_host.blank? %> + host: <%= db_host %> <% end %> -<% when 'postgres', 'postgresql' %> -<% db_host = ENV['DB_HOST'] %> + encoding: utf8 +<% when 'postgres' %> test: adapter: postgresql - database: <%= database_prefix %>_test - username: postgres - min_messages: warning -<% unless db_host.blank? %> + database: <%= db_prefix %>_test + username: <%= db_username.presence || "postgres" %> + <% unless db_password.blank? %> + password: <%= db_password %> + <% end %> + <% unless db_host.blank? %> host: <%= db_host %> -<% end %> -<% when 'sqlite', '', nil %> + <% end %> + min_messages: warning +<% when 'sqlite' %> test: adapter: sqlite3 - database: db/<%= database_prefix %>_test.sqlite3 + database: db/<%= db_prefix %>_test.sqlite3 timeout: 10000 -<% else %> - <% raise "Invalid DB specified: #{ENV['DB']}" %> <% end %> diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000000..96f5e9b9074 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,62 @@ +version: '3.7' + +services: + mysql: + image: mysql:8.0 + command: --default-authentication-plugin=mysql_native_password + environment: + MYSQL_ROOT_PASSWORD: password + volumes: + - mysql:/var/lib/mysql:cached + + postgres: + image: postgres:13.2 + environment: + POSTGRES_USER: root + POSTGRES_PASSWORD: password + volumes: + - postgres:/var/lib/postgresql/data:cached + + app: + build: + context: .dockerdev + dockerfile: Dockerfile + args: + RUBY_VERSION: "2.7.2" + PG_VERSION: 13 + NODE_VERSION: 14 + MYSQL_VERSION: "8.0" + BUNDLER_VERSION: 2 + image: solidus-3.0.0.rc1 + command: bash -c "(bundle check || bundle) && tail -f /dev/null" + environment: + CAPYBARA_DRIVER: selenium_chrome_headless_docker_friendly + DB_USERNAME: root + DB_PASSWORD: password + RAILS_VERSION: ${RAILS_VERSION:-~> 6.1.0} + DB_ALL: "1" + DB_MYSQL_HOST: mysql + DB_POSTGRES_HOST: postgres + HISTFILE: "/home/solidus_user/history/bash_history" + MYSQL_HISTFILE: "/home/solidus_user/history/mysql_history" + RAILS_ENV: development + ports: + - "${SANDBOX_PORT:-3000}:${SANDBOX_PORT:-3000}" + volumes: + - .:/home/solidus_user/app:delegated + - bundle:/home/solidus_user/gems:cached + - history:/home/solidus_user/history:cached + - .dockerdev/.psqlrc:/home/solidus_user/.psqlrc:cached + tty: true + stdin_open: true + tmpfs: + - /tmp + depends_on: + - mysql + - postgres + +volumes: + bundle: + history: + postgres: + mysql: diff --git a/frontend/spec/spec_helper.rb b/frontend/spec/spec_helper.rb index 5f74af6e691..f292b9c01b1 100644 --- a/frontend/spec/spec_helper.rb +++ b/frontend/spec/spec_helper.rb @@ -45,6 +45,15 @@ require "selenium/webdriver" require 'webdrivers' +Capybara.register_driver :selenium_chrome_headless_docker_friendly do |app| + browser_options = ::Selenium::WebDriver::Chrome::Options.new + browser_options.args << '--headless' + browser_options.args << '--disable-gpu' + # Sandbox cannot be used inside unprivileged Docker container + browser_options.args << '--no-sandbox' + browser_options.args << '--window-size=1240,1400' + Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options) +end Capybara.javascript_driver = (ENV['CAPYBARA_DRIVER'] || :selenium_chrome_headless).to_sym ActiveJob::Base.queue_adapter = :test diff --git a/guides/source/developers/getting-started/develop-solidus.html.md b/guides/source/developers/getting-started/develop-solidus.html.md index 7bc449e6270..4354e5ee6a9 100644 --- a/guides/source/developers/getting-started/develop-solidus.html.md +++ b/guides/source/developers/getting-started/develop-solidus.html.md @@ -17,6 +17,8 @@ cd solidus bundle install ``` +Alternatively, you can use the [docker setup](#develop-with-docker). + ## Create a sandbox application Solidus is meant to be run within a Rails application. You can create a sandbox @@ -137,3 +139,68 @@ start creating a new Solidus extension. Check out the doc on [extensions]: http://extensions.solidus.io [writing-extensions]: https://guides.solidus.io/developers/extensions/writing-extensions.html [solidus_dev_support]: https://github.com/solidusio/solidus_dev_support + +## Develop with docker + +If you have docker and docker-compose, you can leverage them to get a +development environment ready to go in a snap: + +```bash +docker-compose up -d +``` + +The `app` service is the one sharing the repository source code. Once the image +has been built, you have to wait for all the dependencies to be installed. This +won't happen in subsequent invocations. You can check progress through the +service's logs: + +```bash +docker-compose logs -f app +``` + +The image can be built with other ruby versions through the `RUBY_VERSION` build argument: + +```bash +docker-compose build --build-arg RUBY_VERSION=2.6 app +docker-compose up -d +``` + +In addition, the rails version can also be set on container initialization +through the `RAILS_VERSION` environment variable: + +```bash +RAILS_VERSION='~> 5.0' docker-compose up -d +``` + +You can use either postgres, mysql or sqlite to run the test suite: + +```bash +# sqlite +docker-compose exec app bin/rspec +# postgres +docker-compose exec app env DB=postgres bin/rspec +# mysql +docker-compose exec app env DB=mysql bin/rspec +``` + +Database engine clients are also available: + +```bash +# sqlite +docker-compose exec app sqlite3 /path/to/db +# postgres +docker-compose exec app env PGPASSWORD=password psql -U root -h postgres +# mysql +docker-compose exec app mysql -u root -h mysql -ppassword +``` + +In order to be able to access the [sandbox +application](#create-a-sandbox-application), just make sure to provide the +appropriate `--binding` option to `rails server`. By default, port `3000` is +exposed, but you can change it through `SANDBOX_PORT` environment variable: + +```bash +SANDBOX_PORT=4000 docker-compose up -d +docker-compose exec app bin/sandbox +docker-compose exec app bin/rails server --binding 0.0.0.0 --port 4000 +```