From 6f4e302f305719645d12c6c0e33ac7679241b59a Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Fri, 28 Jan 2022 14:39:35 +0100 Subject: [PATCH] =?UTF-8?q?configuration=20du=20d=C3=A9ploiement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + Capfile | 44 +++++++++++++++ Gemfile | 13 +++++ Gemfile.lock | 37 +++++++++++++ bin/to_production | 33 +++++++++++ bin/to_staging | 17 ++++++ config/database.yml | 4 ++ config/deploy.rb | 45 +++++++++++++++ config/deploy/config.example.yml | 21 +++++++ config/deploy/production.rb | 75 +++++++++++++++++++++++++ config/deploy/staging.rb | 78 ++++++++++++++++++++++++++ config/environments/staging.rb | 95 ++++++++++++++++++++++++++++++++ 12 files changed, 465 insertions(+) create mode 100644 Capfile create mode 100755 bin/to_production create mode 100755 bin/to_staging create mode 100644 config/deploy.rb create mode 100644 config/deploy/config.example.yml create mode 100644 config/deploy/production.rb create mode 100644 config/deploy/staging.rb create mode 100644 config/environments/staging.rb diff --git a/.gitignore b/.gitignore index a34dab1..61abf0e 100644 --- a/.gitignore +++ b/.gitignore @@ -34,5 +34,8 @@ # Ignore master key for decrypting credentials and more. /config/master.key +# Capistrano deployment configuration +/config/deploy/config.yml + /app/assets/builds/* !/app/assets/builds/.keep diff --git a/Capfile b/Capfile new file mode 100644 index 0000000..c967779 --- /dev/null +++ b/Capfile @@ -0,0 +1,44 @@ +# Load DSL and set up stages +require "capistrano/setup" + +# Include default deployment tasks +require "capistrano/deploy" + +# Load the SCM plugin appropriate to your project: +# +# require "capistrano/scm/hg" +# install_plugin Capistrano::SCM::Hg +# or +# require "capistrano/scm/svn" +# install_plugin Capistrano::SCM::Svn +# or +require "capistrano/scm/git" +install_plugin Capistrano::SCM::Git + +# 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/rvm" +require "capistrano/rbenv" +# require "capistrano/chruby" +require "capistrano/bundler" +require "capistrano/rails/assets" +require "capistrano/rails/migrations" +# require "capistrano/passenger" +require "capistrano/puma" +install_plugin Capistrano::Puma # Default puma tasks +# install_plugin Capistrano::Puma::Workers # if you want to control the workers (in cluster mode) +# install_plugin Capistrano::Puma::Jungle # if you need the jungle tasks +# install_plugin Capistrano::Puma::Monit # if you need the monit tasks +# install_plugin Capistrano::Puma::Nginx # if you want to upload a nginx site template + +# Load custom tasks from `lib/capistrano/tasks` if you have any defined +Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r } diff --git a/Gemfile b/Gemfile index a789554..cc9a362 100644 --- a/Gemfile +++ b/Gemfile @@ -73,6 +73,19 @@ group :development do # Speed up commands on slow machines / big apps [https://github.com/rails/spring] # gem "spring" + + gem "capistrano", "~> 3.16", require: false + gem "capistrano-rails", require: false + gem "capistrano-rbenv", require: false + gem 'capistrano3-puma', require: false + + # Necessary for elliptic curve SSH keys + # gem 'rbnacl', ">= 3.2", "<5.0" + # gem 'rbnacl-libsodium' + gem 'bcrypt_pbkdf', ">= 1.0", "<2.0" + gem 'ed25519', ">= 1.2", "< 2.0" + gem "openssl", "< 3.0.0" + end group :test do diff --git a/Gemfile.lock b/Gemfile.lock index 3b80a40..47c808c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -68,12 +68,32 @@ GEM tzinfo (~> 2.0) addressable (2.8.0) public_suffix (>= 2.0.2, < 5.0) + airbrussh (1.4.0) + sshkit (>= 1.6.1, != 1.7.0) ast (2.4.2) bcrypt (3.1.16) + bcrypt_pbkdf (1.1.0) bindex (0.8.1) bootsnap (1.10.2) msgpack (~> 1.2) builder (3.2.4) + capistrano (3.16.0) + airbrussh (>= 1.0.0) + i18n + rake (>= 10.0.0) + sshkit (>= 1.9.0) + capistrano-bundler (2.0.1) + capistrano (~> 3.1) + capistrano-rails (1.6.1) + capistrano (~> 3.1) + capistrano-bundler (>= 1.1, < 3) + capistrano-rbenv (2.2.0) + capistrano (~> 3.1) + sshkit (~> 1.3) + capistrano3-puma (5.2.0) + capistrano (~> 3.7) + capistrano-bundler + puma (>= 4.0, < 6.0) capybara (3.36.0) addressable matrix @@ -90,6 +110,7 @@ GEM irb (>= 1.3.6) reline (>= 0.2.7) digest (3.1.0) + ed25519 (1.3.0) erubi (1.10.0) globalid (1.0.0) activesupport (>= 5.0) @@ -102,6 +123,7 @@ GEM railties (>= 6.0.0) io-console (0.5.11) io-wait (0.2.1) + ipaddr (1.2.3) irb (1.4.1) reline (>= 0.3.0) jbuilder (2.11.5) @@ -141,13 +163,18 @@ GEM net-protocol (0.1.2) io-wait timeout + net-scp (3.0.0) + net-ssh (>= 2.6.5, < 7.0.0) net-smtp (0.3.1) digest net-protocol timeout + net-ssh (6.1.0) nio4r (2.5.8) nokogiri (1.13.1-x86_64-linux) racc (~> 1.4) + openssl (2.2.1) + ipaddr parallel (1.21.0) parser (3.1.0.0) ast (~> 2.4.1) @@ -225,6 +252,9 @@ GEM activesupport (>= 5.2) sprockets (>= 3.0.0) sqlite3 (1.4.2) + sshkit (1.21.2) + net-scp (>= 1.1.2) + net-ssh (>= 2.8.0) standard (1.7.0) rubocop (= 1.25.0) rubocop-performance (= 1.13.2) @@ -262,13 +292,20 @@ PLATFORMS DEPENDENCIES bcrypt (~> 3.1.7) + bcrypt_pbkdf (>= 1.0, < 2.0) bootsnap + capistrano (~> 3.16) + capistrano-rails + capistrano-rbenv + capistrano3-puma capybara debug + ed25519 (>= 1.2, < 2.0) heroicon importmap-rails jbuilder kaminari (~> 1.2.2) + openssl (< 3.0.0) puma (~> 5.0) pundit (~> 2.1.1) rails (~> 7.0.0) diff --git a/bin/to_production b/bin/to_production new file mode 100755 index 0000000..3aa9f82 --- /dev/null +++ b/bin/to_production @@ -0,0 +1,33 @@ +#!/bin/sh + +set -e +[ -n "$DEBUG" ] && set -x + +echo "๐Ÿ‘€ Fetching git repository informationโ€ฆ" +git fetch origin --quiet + +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) +CURRENT_HEAD=$(git rev-parse HEAD) +ORIGIN_HEAD=$(git rev-parse origin/main) + +if [ "main" != "${CURRENT_BRANCH}" ]; then + echo "๐Ÿ˜• Only main can be deployed to production" + exit 1 +fi + +if [ "${CURRENT_HEAD}" != "${ORIGIN_HEAD}" ]; then + echo "๐Ÿ˜• Local main is not up to date with origin" + exit 1 +fi + +read -r -p "๐Ÿ‘‰ Deploy main to production ? (y/N) " confirmed + +if [ "${confirmed}" != "y" ]; then + echo "๐Ÿ˜ž Abort, bye." + exit 1 +fi + +echo "๐Ÿš€ Deploying main to production !" +bundle exec cap production deploy + +echo "๐Ÿ˜Ž Done !" diff --git a/bin/to_staging b/bin/to_staging new file mode 100755 index 0000000..71803bd --- /dev/null +++ b/bin/to_staging @@ -0,0 +1,17 @@ +#!/bin/sh + +set -e +[ -n "$DEBUG" ] && set -x + +echo "๐Ÿ‘€ Fetching git repository informationโ€ฆ" +git fetch origin --quiet + +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) + +echo "๐Ÿš€ Deploying ${CURRENT_BRANCH} to staging !" +git branch -f staging +git push --force origin staging + +bundle exec cap staging deploy + +echo "๐Ÿ˜Ž Done !" diff --git a/config/database.yml b/config/database.yml index fcba57f..56b616c 100644 --- a/config/database.yml +++ b/config/database.yml @@ -23,3 +23,7 @@ test: production: <<: *default database: db/production.sqlite3 + +staging: + <<: *default + database: db/staging.sqlite3 diff --git a/config/deploy.rb b/config/deploy.rb new file mode 100644 index 0000000..9e71b92 --- /dev/null +++ b/config/deploy.rb @@ -0,0 +1,45 @@ +# config valid for current version and patch releases of Capistrano +lock "~> 3.16.0" + +set :application, "evodata" +set :repo_url, "git@gitea.evolix.org:evolix/evodata.git" + +# 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 :format is :airbrussh. +# set :format, :airbrussh + +# You can configure the Airbrussh format using :format_options. +# These are the defaults. +# set :format_options, command_output: true, log_file: "log/capistrano.log", color: :auto, truncate: :auto + +# Default value for :pty is false +# set :pty, true + +# Default value for :linked_files is [] +# append :linked_files, "config/database.yml" + +# Default value for linked_dirs is [] +# append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "public/system" + +append :linked_dirs, 'log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', '.bundle', 'public/system', 'public/uploads' +append :linked_files, 'config/master.key' + +# Default value for default_env is {} +# set :default_env, { path: "/opt/ruby/bin:$PATH" } + +# Default value for local_user is ENV['USER'] +# set :local_user, -> { `git config user.name`.chomp } + +# Default value for keep_releases is 5 +# set :keep_releases, 5 + +# Uncomment the following to require manually verifying the host key before first deploy. +# set :ssh_options, verify_host_key: :secure + +Net::SSH::Transport::Algorithms::ALGORITHMS.values.each { |algs| algs.reject! { |a| a =~ /^ecd(sa|h)-sha2/ } } +Net::SSH::KnownHosts::SUPPORTED_TYPE.reject! { |t| t =~ /^ecd(sa|h)-sha2/ } \ No newline at end of file diff --git a/config/deploy/config.example.yml b/config/deploy/config.example.yml new file mode 100644 index 0000000..1f3062b --- /dev/null +++ b/config/deploy/config.example.yml @@ -0,0 +1,21 @@ +staging: + deploy_to: /var/www/my_app_name + servers: + - + host: staging.example.com + user: deploy + roles: + - app + - db + - web + +production: + deploy_to: /var/www/my_app_name + servers: + - + host: www.example.com + user: deploy + roles: + - app + - db + - web \ No newline at end of file diff --git a/config/deploy/production.rb b/config/deploy/production.rb new file mode 100644 index 0000000..d759b94 --- /dev/null +++ b/config/deploy/production.rb @@ -0,0 +1,75 @@ +config = YAML::load_file(File.join(__dir__, 'config.yml')).fetch("production") + +config.fetch("servers").each do |sc| + server sc.fetch("host"), user: sc.fetch("user"), roles: sc.fetch("roles") +end + +set :deploy_to, config.fetch("deploy_to") +set :branch, "staging" + +if config.has_key?(:keep_releases) + set :keep_releases, config.fetch("keep_releases").to_i +end + + +# server-based syntax +# ====================== +# Defines a single server with a list of roles and multiple properties. +# You can define all roles on a single server, or split them: + +# server "example.com", user: "deploy", roles: %w{app db web}, my_property: :my_value +# server "example.com", user: "deploy", roles: %w{app web}, other_property: :other_value +# server "db.example.com", user: "deploy", roles: %w{db} + + + +# role-based syntax +# ================== + +# Defines a role with one or multiple servers. The primary server in each +# group is considered to be the first unless any hosts have the primary +# property set. Specify the username and a domain or IP for the server. +# Don't use `:all`, it's a meta role. + +# role :app, %w{deploy@example.com}, my_property: :my_value +# role :web, %w{user1@primary.com user2@additional.com}, other_property: :other_value +# role :db, %w{deploy@example.com} + + + +# Configuration +# ============= +# You can set any configuration variable like in config/deploy.rb +# These variables are then only loaded and set in this stage. +# For available Capistrano configuration variables see the documentation page. +# http://capistranorb.com/documentation/getting-started/configuration/ +# Feel free to add new variables to customise your setup. + + + +# Custom SSH Options +# ================== +# You may pass any option but keep in mind that net/ssh understands a +# limited set of options, consult the Net::SSH documentation. +# http://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start +# +# Global options +# -------------- +# set :ssh_options, { +# keys: %w(/home/user_name/.ssh/id_rsa), +# forward_agent: false, +# auth_methods: %w(password) +# } +# +# The server-based syntax can be used to override options: +# ------------------------------------ +# server "example.com", +# user: "user_name", +# roles: %w{web app}, +# ssh_options: { +# user: "user_name", # overrides user setting above +# keys: %w(/home/user_name/.ssh/id_rsa), +# forward_agent: false, +# auth_methods: %w(publickey password) +# # password: "please use keys" +# } diff --git a/config/deploy/staging.rb b/config/deploy/staging.rb new file mode 100644 index 0000000..c92ab52 --- /dev/null +++ b/config/deploy/staging.rb @@ -0,0 +1,78 @@ +config = YAML::load_file(File.join(__dir__, 'config.yml')).fetch("staging") + +config.fetch("servers").each do |sc| + server sc.fetch("host"), user: sc.fetch("user"), roles: sc.fetch("roles") +end + +set :deploy_to, config.fetch("deploy_to") +set :branch, "staging" + +if config.has_key?("keep_releases") + set :keep_releases, config.fetch("keep_releases").to_i +end + + +if config.has_key?("tmp_dir") + set :tmp_dir, config.fetch("tmp_dir") +end + + +# server-based syntax +# ====================== +# Defines a single server with a list of roles and multiple properties. +# You can define all roles on a single server, or split them: + +# server "example.com", user: "deploy", roles: %w{app db web}, my_property: :my_value +# server "example.com", user: "deploy", roles: %w{app web}, other_property: :other_value +# server "db.example.com", user: "deploy", roles: %w{db} + +# role-based syntax +# ================== + +# Defines a role with one or multiple servers. The primary server in each +# group is considered to be the first unless any hosts have the primary +# property set. Specify the username and a domain or IP for the server. +# Don't use `:all`, it's a meta role. + +# role :app, %w{deploy@example.com}, my_property: :my_value +# role :web, %w{user1@primary.com user2@additional.com}, other_property: :other_value +# role :db, %w{deploy@example.com} + + + +# Configuration +# ============= +# You can set any configuration variable like in config/deploy.rb +# These variables are then only loaded and set in this stage. +# For available Capistrano configuration variables see the documentation page. +# http://capistranorb.com/documentation/getting-started/configuration/ +# Feel free to add new variables to customise your setup. + + + +# Custom SSH Options +# ================== +# You may pass any option but keep in mind that net/ssh understands a +# limited set of options, consult the Net::SSH documentation. +# http://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start +# +# Global options +# -------------- +# set :ssh_options, { +# keys: %w(/home/user_name/.ssh/id_rsa), +# forward_agent: false, +# auth_methods: %w(password) +# } +# +# The server-based syntax can be used to override options: +# ------------------------------------ +# server "example.com", +# user: "user_name", +# roles: %w{web app}, +# ssh_options: { +# user: "user_name", # overrides user setting above +# keys: %w(/home/user_name/.ssh/id_rsa), +# forward_agent: false, +# auth_methods: %w(publickey password) +# # password: "please use keys" +# } diff --git a/config/environments/staging.rb b/config/environments/staging.rb new file mode 100644 index 0000000..7cbbe08 --- /dev/null +++ b/config/environments/staging.rb @@ -0,0 +1,95 @@ +require "active_support/core_ext/integer/time" + +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # Code is not reloaded between requests. + config.cache_classes = true + + # 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 and caching is turned on. + config.consider_all_requests_local = false + config.action_controller.perform_caching = true + + # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] + # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). + # config.require_master_key = true + + # Disable serving static files from the `/public` folder by default since + # Apache or NGINX already handles this. + config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present? + + # Compress CSS using a preprocessor. + # config.assets.css_compressor = :sass + + # Do not fallback 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.*/ ] + + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. + # config.force_ssl = true + + # Include generic and useful information about system operation, but avoid logging too much + # information to avoid inadvertent exposure of personally identifiable information (PII). + config.log_level = :info + + # Prepend all log lines with the following tags. + 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_name_prefix = "evocheck_web_production" + + config.action_mailer.perform_caching = false + + config.action_mailer.default_url_options = { host: 'staging.evodata.evolix.eu' } + + # 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 + + # 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 + + # Use default logging formatter so that PID and timestamp are not suppressed. + config.log_formatter = ::Logger::Formatter.new + + # Use a different logger for distributed setups. + # require "syslog/logger" + # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new "app-name") + + if ENV["RAILS_LOG_TO_STDOUT"].present? + logger = ActiveSupport::Logger.new(STDOUT) + logger.formatter = config.log_formatter + config.logger = ActiveSupport::TaggedLogging.new(logger) + end + + # Do not dump schema after migrations. + config.active_record.dump_schema_after_migration = false +end