├── .ansible-deploy ├── ansible.cfg ├── app-vars.yml ├── deploy.yml ├── deploy_tasks │ └── after_cleanup.yml ├── group_vars │ └── .DS_Store ├── inventories │ └── development.ini ├── provision.yml ├── roles │ ├── certbot │ │ ├── defaults │ │ │ └── main.yml │ │ └── tasks │ │ │ ├── cert.yml │ │ │ └── main.yml │ ├── common │ │ ├── defaults │ │ │ └── main.yml │ │ └── tasks │ │ │ └── main.yml │ ├── fluentbit │ │ ├── defaults │ │ │ └── main.yml │ │ ├── handlers │ │ │ └── main.yml │ │ ├── tasks │ │ │ └── main.yml │ │ └── templates │ │ │ └── td-agent-bit.conf.j2 │ ├── logrotate │ │ ├── defaults │ │ │ └── main.yml │ │ └── tasks │ │ │ └── main.yml │ ├── nginx │ │ ├── defaults │ │ │ └── main.yml │ │ ├── files │ │ │ ├── config │ │ │ │ ├── general.conf │ │ │ │ ├── letsencrypt.conf │ │ │ │ ├── proxy.conf │ │ │ │ └── security.conf │ │ │ └── nginx.conf │ │ ├── handlers │ │ │ └── main.yml │ │ ├── tasks │ │ │ └── main.yml │ │ └── templates │ │ │ └── nginx-default.conf.j2 │ ├── nodejs │ │ ├── defaults │ │ │ └── main.yml │ │ └── tasks │ │ │ └── main.yml │ ├── pgbackup │ │ ├── defaults │ │ │ └── main.yml │ │ ├── tasks │ │ │ └── main.yml │ │ └── templates │ │ │ └── postgresql-backup.j2 │ ├── postgresql │ │ ├── defaults │ │ │ └── main.yml │ │ ├── handlers │ │ │ └── main.yml │ │ ├── tasks │ │ │ └── main.yml │ │ └── templates │ │ │ ├── pg_hba.conf.j2 │ │ │ └── postgresql.conf.j2 │ ├── puma │ │ └── tasks │ │ │ └── main.yml │ ├── redis │ │ └── tasks │ │ │ └── main.yml │ ├── ruby │ │ ├── defaults │ │ │ └── main.yml │ │ └── tasks │ │ │ └── main.yml │ ├── sidekiq │ │ └── tasks │ │ │ └── main.yml │ ├── ssh │ │ ├── defaults │ │ │ └── main.yml │ │ ├── handlers │ │ │ └── main.yml │ │ └── tasks │ │ │ └── main.yml │ ├── ufw │ │ ├── defaults │ │ │ └── main.yml │ │ ├── handlers │ │ │ └── main.yml │ │ └── tasks │ │ │ └── main.yml │ ├── user │ │ ├── defaults │ │ │ └── main.yml │ │ └── tasks │ │ │ └── main.yml │ └── yarn │ │ └── tasks │ │ └── main.yml └── templates │ ├── .rbenv-vars.j2 │ ├── database.yml.j2 │ ├── nginx.conf.j2 │ ├── puma.service.j2 │ └── sidekiq.service.j2 ├── .browserslistrc ├── .gitattributes ├── .gitignore ├── .ruby-version ├── Gemfile ├── Gemfile.lock ├── README.md ├── Rakefile ├── app ├── assets │ ├── config │ │ └── manifest.js │ ├── images │ │ └── .keep │ └── stylesheets │ │ ├── application.css │ │ └── index.scss ├── channels │ └── application_cable │ │ ├── channel.rb │ │ └── connection.rb ├── controllers │ ├── application_controller.rb │ ├── concerns │ │ └── .keep │ └── index_controller.rb ├── helpers │ ├── application_helper.rb │ └── index_helper.rb ├── javascript │ ├── channels │ │ ├── consumer.js │ │ └── index.js │ └── packs │ │ └── application.js ├── jobs │ └── application_job.rb ├── mailers │ └── application_mailer.rb ├── models │ ├── application_record.rb │ └── concerns │ │ └── .keep ├── views │ ├── index │ │ └── index.html.erb │ └── layouts │ │ ├── application.html.erb │ │ ├── mailer.html.erb │ │ └── mailer.text.erb └── workers │ └── hard_worker.rb ├── babel.config.js ├── bin ├── bundle ├── rails ├── rake ├── setup ├── spring ├── webpack ├── webpack-dev-server └── yarn ├── config.ru ├── config ├── application.rb ├── boot.rb ├── cable.yml ├── credentials.yml.enc ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── application_controller_renderer.rb │ ├── assets.rb │ ├── backtrace_silencers.rb │ ├── content_security_policy.rb │ ├── cookies_serializer.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── mime_types.rb │ ├── permissions_policy.rb │ ├── sidekiq.rb │ └── wrap_parameters.rb ├── locales │ └── en.yml ├── puma.rb ├── routes.rb ├── sidekiq.yml ├── spring.rb ├── storage.yml ├── webpack │ ├── development.js │ ├── environment.js │ ├── production.js │ └── test.js └── webpacker.yml ├── db └── seeds.rb ├── lib ├── assets │ └── .keep └── tasks │ └── .keep ├── log └── .keep ├── package.json ├── postcss.config.js ├── public ├── 404.html ├── 422.html ├── 500.html ├── apple-touch-icon-precomposed.png ├── apple-touch-icon.png ├── favicon.ico └── robots.txt ├── storage └── .keep ├── test ├── application_system_test_case.rb ├── channels │ └── application_cable │ │ └── connection_test.rb ├── controllers │ ├── .keep │ └── index_controller_test.rb ├── fixtures │ └── files │ │ └── .keep ├── helpers │ └── .keep ├── integration │ └── .keep ├── mailers │ └── .keep ├── models │ └── .keep ├── system │ └── .keep ├── test_helper.rb └── workers │ └── hard_worker_test.rb ├── tmp ├── .keep └── pids │ └── .keep ├── vendor └── .keep └── yarn.lock /.ansible-deploy/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | host_key_checking = False 3 | vault_password_file = ./.vault_pass -------------------------------------------------------------------------------- /.ansible-deploy/app-vars.yml: -------------------------------------------------------------------------------- 1 | --- 2 | app_name: YOUR_APP_NAME 3 | deploy_user: deployer 4 | deploy_group: "{{ deploy_user }}" 5 | deploy_user_path: "/home/{{ deploy_user }}" 6 | 7 | # App Git repo 8 | app_git_repo: "git@github.com:pedropaf/rails-ansible-provision-deployment.git" 9 | app_git_branch: "main" 10 | 11 | # Rails app 12 | app_root_path: "{{ deploy_user_path }}/{{ app_name }}" 13 | app_current_path: "{{ app_root_path }}/current" 14 | app_releases_path: "{{ app_root_path }}/releases" 15 | app_shared_path: "{{ app_root_path }}/shared" 16 | app_pids_path: "{{ app_shared_path }}/tmp/pids" 17 | app_logs_path: "{{ app_shared_path }}/logs" 18 | app_sockets_path: "{{ app_shared_path }}/sockets" 19 | rails_db_pool: 20 20 | rails_environment: production 21 | 22 | # Puma 23 | puma_service_file: "puma.service.j2" 24 | puma_config_file: "{{ app_current_path }}/config/puma.rb" 25 | puma_socket: "{{ app_sockets_path }}/puma.sock" 26 | puma_web_concurrency: 2 27 | 28 | # Sidekiq 29 | sidekiq_service_file: "sidekiq.service.j2" 30 | 31 | # Ansistrano 32 | ansistrano_deploy_via: git 33 | ansistrano_git_repo: "{{ app_git_repo }}" 34 | ansistrano_git_branch: "{{ app_git_branch }}" 35 | ansistrano_git_identity_key_path: "~/.ssh/id_rsa" 36 | ansistrano_deploy_from: "{{ playbook_dir }}/../" 37 | ansistrano_deploy_to: "{{ app_root_path }}" 38 | ansistrano_keep_releases: 3 39 | ansistrano_after_cleanup_tasks_file: "{{ playbook_dir }}/deploy_tasks/after_cleanup.yml" 40 | ansistrano_ensure_shared_paths_exist: yes 41 | ansistrano_ensure_basedirs_shared_files_exist: yes 42 | 43 | ansistrano_shared_paths: 44 | - log # log -> ../../shared/log 45 | - tmp # tmp -> ../../shared/tmp 46 | - vendor # vendor -> ../../shared/vendor 47 | - public/assets # For rails asset pipeline 48 | - public/packs # For webpacker 49 | - node_modules # For webpacker node_modules -> ../../shared/node_modules 50 | 51 | shared_files_to_copy: 52 | - { src: database.yml.j2, dest: config/database.yml } 53 | 54 | # Common 55 | required_packages: 56 | - zlib1g-dev 57 | - build-essential 58 | - libssl-dev 59 | - libreadline-dev 60 | - libyaml-dev 61 | - libxml2-dev 62 | - libxslt1-dev 63 | - libcurl4-openssl-dev 64 | - libffi-dev 65 | - dirmngr 66 | - gnupg 67 | - autoconf 68 | - bison 69 | - libreadline6-dev 70 | - libncurses5-dev 71 | - libgdbm6 72 | - libgdbm-dev 73 | - libpq-dev # postgresql client 74 | - libjemalloc-dev # jemalloc 75 | - acl 76 | - libsqlite3-dev 77 | 78 | # Ruby 79 | ruby_version: 2.7.2 80 | rbenv_ruby_configure_opts: "RUBY_CONFIGURE_OPTS=--with-jemalloc" 81 | rbenv_root_path: "{{ deploy_user_path }}/.rbenv" 82 | rbenv_shell_rc_path: "{{ deploy_user_path }}/.bashrc" 83 | rubies_path: "{{ rbenv_root_path }}/versions" 84 | ruby_path: "{{ rubies_path }}/{{ ruby_version }}" 85 | rbenv_bin: "{{ rbenv_root_path }}/bin/rbenv" 86 | rbenv_bundle: "{{ rbenv_root_path }}/shims/bundle" 87 | 88 | # Nodejs 89 | nodejs_version: "15.x" 90 | 91 | # Postgresql 92 | postgresql_version: "13" 93 | postgresql_db_user: "{{ deploy_user }}_postgresql_user" 94 | postgresql_db_password: "{{ vault_postgresql_db_password }}" # from vault 95 | postgresql_db_name: "{{ app_name }}_production" 96 | postgresql_listen: 97 | - "localhost" 98 | - "{{ ansible_default_ipv4.address }}" # only if db is on a separate server 99 | 100 | # nginx 101 | nginx_https_enabled: true 102 | nginx_conf_template: "nginx.conf.j2" 103 | 104 | # certbot 105 | certbot_email: "pedro@pedroalonso.net" 106 | certbot_domains: 107 | - "demo-ansible.pedroalonso.net" 108 | 109 | # PostgreSQL Backup to S3 110 | aws_key: "{{ vault_aws_key }}" 111 | aws_secret: "{{ vault_aws_secret }}" 112 | 113 | postgresql_backup_dir: "{{ deploy_user_path }}/backups" 114 | postgresql_backup_filename_format: >- 115 | {{ app_name }}-%Y%m%d-%H%M%S.pgdump 116 | postgresql_db_backup_healthcheck: "NOTIFICATION_URL (eg: https://healthcheck.io/)" 117 | postgresql_s3_backup_bucket: "DB_BACKUP_BUCKET" 118 | postgresql_s3_backup_hour: "3" 119 | postgresql_s3_backup_minute: "*" 120 | postgresql_s3_backup_delete_after: "7 days" # days after which old backups should be deleted 121 | 122 | # fluentbit 123 | fluentbit_inputs: 124 | - Name: tail 125 | Path: "{{ app_logs_path }}/production.log" 126 | 127 | fluentbit_outputs: 128 | - Name: http 129 | Match: "*" 130 | tls: On 131 | Host: "" # e.g: loggly or sumologic logs endpoint 132 | Port: 443 133 | URI: "" # e.g: /receiver/v1/http/{{ vault_sumologic_token }} 134 | Format: json_lines 135 | Json_Date_Key: timestamp 136 | Json_Date_Format: iso8601 137 | Retry_Limit: False 138 | 139 | logrotate_conf: 140 | - path: "ansible" 141 | conf: | 142 | "{{ app_current_path }}/log/*.log" { 143 | weekly 144 | size 100M 145 | missingok 146 | rotate 12 147 | compress 148 | delaycompress 149 | notifempty 150 | copytruncate 151 | } 152 | 153 | # UFW 154 | ufw_rules: 155 | - { rule: "allow", proto: "tcp", from: "any", port: "80" } 156 | - { rule: "allow", proto: "tcp", from: "any", port: "443" } 157 | - { rule: "allow", proto: "tcp", from: "any", port: "22" } -------------------------------------------------------------------------------- /.ansible-deploy/deploy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Deploy our Rails app 3 | hosts: all 4 | become: true 5 | become_user: "{{ deploy_user }}" 6 | 7 | vars_files: 8 | - app-vars.yml 9 | 10 | pre_tasks: 11 | - name: Setup app folder 12 | file: 13 | state: directory 14 | path: "{{ app_root_path }}" 15 | owner: "{{ deploy_user }}" 16 | group: "{{ deploy_group }}" 17 | 18 | - name: Copy rbenv-vars file 19 | template: 20 | src: ".rbenv-vars.j2" 21 | dest: "{{ app_root_path }}/.rbenv-vars" 22 | owner: "{{ deploy_user }}" 23 | group: "{{ deploy_group }}" 24 | 25 | - name: Make shared directories 26 | file: 27 | path: "{{ app_shared_path }}/{{ item }}" 28 | state: directory 29 | owner: "{{ deploy_user }}" 30 | group: "{{ deploy_group }}" 31 | with_items: 32 | - tmp 33 | - tmp/pids 34 | - tmp/cache 35 | - sockets 36 | - log 37 | - public 38 | - public/packs 39 | - vendor 40 | - vendor/bundle 41 | - bin 42 | - config 43 | - config/puma 44 | - assets 45 | - node_modules 46 | 47 | - name: Upload shared files 48 | template: 49 | src: "{{ item.src }}" 50 | dest: "{{ app_shared_path }}/{{ item.dest }}" 51 | owner: "{{ deploy_user }}" 52 | group: "{{ deploy_group }}" 53 | with_items: "{{ shared_files_to_copy }}" 54 | tags: 55 | - copy 56 | 57 | roles: 58 | - role: ansistrano.deploy 59 | 60 | - role: puma 61 | tags: puma 62 | become: true 63 | become_user: root 64 | 65 | - role: sidekiq 66 | tags: sidekiq 67 | become: true 68 | become_user: root -------------------------------------------------------------------------------- /.ansible-deploy/deploy_tasks/after_cleanup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Bundle install with --deploy 3 | bundler: 4 | state: present 5 | deployment_mode: yes 6 | gem_path: "../../shared/vendor/bundle" # relative to chdir 7 | chdir: "{{ ansistrano_release_path.stdout }}" 8 | exclude_groups: ["development", "test"] 9 | executable: "{{ rbenv_bundle }}" 10 | 11 | - name: Running pending migrations 12 | shell: "{{ rbenv_bundle }} exec rake db:migrate" 13 | run_once: true 14 | args: 15 | chdir: "{{ ansistrano_release_path.stdout }}" 16 | 17 | - name: Precompiling assets 18 | shell: "{{ rbenv_bundle }} exec rake assets:precompile" 19 | args: 20 | chdir: "{{ ansistrano_release_path.stdout }}" -------------------------------------------------------------------------------- /.ansible-deploy/group_vars/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedropaf/rails-ansible-provision-deployment/68b57fd549b58ea596a0badfaf929712d5e39d36/.ansible-deploy/group_vars/.DS_Store -------------------------------------------------------------------------------- /.ansible-deploy/inventories/development.ini: -------------------------------------------------------------------------------- 1 | [web] 2 | 34.234.69.254 3 | 4 | [all:vars] 5 | ansible_ssh_user=ubuntu 6 | ansible_python_interpreter=/usr/bin/python3 7 | ansible_ssh_private_key_file="~/.ssh/pedroalonso.pem" -------------------------------------------------------------------------------- /.ansible-deploy/provision.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: all 3 | become: true 4 | 5 | vars_files: 6 | - app-vars.yml 7 | 8 | roles: 9 | - role: common 10 | - role: user 11 | - role: ssh 12 | - role: ufw 13 | - role: ruby 14 | tags: ruby 15 | - role: nodejs 16 | tags: nodejs 17 | - role: yarn 18 | tags: nodejs 19 | - role: postgresql 20 | tags: postgresql 21 | - role: redis 22 | tags: redis 23 | - role: nginx 24 | tags: nginx 25 | - role: logrotate 26 | tags: logrotate 27 | - role: certbot 28 | when: nginx_https_enabled == true 29 | tags: certbot -------------------------------------------------------------------------------- /.ansible-deploy/roles/certbot/defaults/main.yml: -------------------------------------------------------------------------------- 1 | certbot_email: admin@example.com 2 | certbot_flags: "" 3 | certbot_domains: [] 4 | -------------------------------------------------------------------------------- /.ansible-deploy/roles/certbot/tasks/cert.yml: -------------------------------------------------------------------------------- 1 | - name: Check if a certificate already exists 2 | stat: 3 | path: /etc/letsencrypt/live/{{ domain | replace('*.', '') }}/cert.pem 4 | register: letsencrypt_cert 5 | 6 | - name: Get new certificate 7 | command: "certbot --nginx -m {{ certbot_email }} -d {{ domain }} --agree-tos {{ certbot_flags }}" 8 | when: not letsencrypt_cert.stat.exists 9 | notify: Restart nginx -------------------------------------------------------------------------------- /.ansible-deploy/roles/certbot/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install snap core 3 | command: snap install core 4 | 5 | - name: Refresh snap core 6 | command: snap refresh core 7 | 8 | - name: Install certbot 9 | command: snap install --classic certbot 10 | 11 | - name: Link certbot 12 | file: 13 | src: "/snap/bin/certbot" 14 | dest: "/usr/bin/certbot" 15 | state: link 16 | 17 | - name: Ensure that nginx is stopped 18 | service: 19 | name: nginx 20 | state: stopped 21 | 22 | - name: Check & get new certificate 23 | include_tasks: cert.yml 24 | loop: "{{ certbot_domains }}" 25 | loop_control: 26 | loop_var: domain 27 | -------------------------------------------------------------------------------- /.ansible-deploy/roles/common/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | required_packages: [] -------------------------------------------------------------------------------- /.ansible-deploy/roles/common/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install prerequisites 3 | apt: name=aptitude update_cache=yes state=latest force_apt_get=yes 4 | 5 | - name: Install required system packages 6 | apt: name={{ item }} state=latest update_cache=yes 7 | loop: 8 | - curl 9 | - ufw 10 | - fail2ban 11 | - git-core 12 | - apt-transport-https 13 | - ca-certificates 14 | - software-properties-common 15 | - python3-pip 16 | - virtualenv 17 | - python3-setuptools 18 | - "{{ required_packages }}" -------------------------------------------------------------------------------- /.ansible-deploy/roles/fluentbit/defaults/main.yml: -------------------------------------------------------------------------------- 1 | fluentbit_flush_seconds: 2 2 | 3 | # Default inputs 4 | fluentbit_inputs: [] 5 | 6 | # Default outputs 7 | fluentbit_outputs: [] 8 | 9 | fluentbit_hostname: "{{ hostname }}" -------------------------------------------------------------------------------- /.ansible-deploy/roles/fluentbit/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Restart fluentbit 3 | service: 4 | name: td-agent-bit 5 | enabled: true 6 | state: restarted 7 | become: true -------------------------------------------------------------------------------- /.ansible-deploy/roles/fluentbit/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Add td-agent-bit apt-key 3 | apt_key: 4 | url: https://packages.fluentbit.io/fluentbit.key 5 | state: present 6 | 7 | - name: Add td-agent-bit repository 8 | apt_repository: 9 | repo: 'deb https://packages.fluentbit.io/ubuntu/bionic bionic main' 10 | state: present 11 | filename: td-agent-bit 12 | update_cache: true 13 | 14 | - name: Install fluentbit package 15 | package: 16 | name: td-agent-bit 17 | state: present 18 | update_cache: true 19 | notify: Restart fluentbit 20 | 21 | - name: Configure td-agent-bit.conf file 22 | template: 23 | src: td-agent-bit.conf.j2 24 | dest: /etc/td-agent-bit/td-agent-bit.conf 25 | mode: 0644 26 | notify: Restart fluentbit -------------------------------------------------------------------------------- /.ansible-deploy/roles/fluentbit/templates/td-agent-bit.conf.j2: -------------------------------------------------------------------------------- 1 | [SERVICE] 2 | Flush {{ fluentbit_flush_seconds }} 3 | 4 | {% for input in fluentbit_inputs %} 5 | [INPUT] 6 | {% for key in input %} 7 | {{ key }} {{ input[key] }} 8 | {% endfor %} 9 | {% endfor %} 10 | 11 | {% for output in fluentbit_outputs %} 12 | [OUTPUT] 13 | {% for key in output %} 14 | {{ key }} {{ output[key] }} 15 | {% endfor %} 16 | {% endfor %} 17 | 18 | [FILTER] 19 | Name record_modifier 20 | Match * 21 | Record hostname {{ fluentbit_hostname }} -------------------------------------------------------------------------------- /.ansible-deploy/roles/logrotate/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | logrotate_conf: [] -------------------------------------------------------------------------------- /.ansible-deploy/roles/logrotate/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install logrotate 3 | apt: name=logrotate state=latest update_cache=yes 4 | 5 | - blockinfile: 6 | path: "/etc/logrotate.d/{{ item.path }}" 7 | block: "{{ item.conf }}" 8 | create: yes 9 | loop: "{{ logrotate_conf }}" -------------------------------------------------------------------------------- /.ansible-deploy/roles/nginx/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | nginx_conf_template: "nginx-default.conf.j2" 3 | nginx_https_enabled: false -------------------------------------------------------------------------------- /.ansible-deploy/roles/nginx/files/config/general.conf: -------------------------------------------------------------------------------- 1 | # favicon.ico 2 | location = /favicon.ico { 3 | log_not_found off; 4 | access_log off; 5 | } 6 | 7 | # robots.txt 8 | location = /robots.txt { 9 | log_not_found off; 10 | access_log off; 11 | } 12 | 13 | # gzip 14 | gzip on; 15 | gzip_vary on; 16 | gzip_proxied any; 17 | gzip_comp_level 6; 18 | gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml; 19 | 20 | # remove trailing slashes 21 | rewrite ^/(.*)/$ /$1 permanent; -------------------------------------------------------------------------------- /.ansible-deploy/roles/nginx/files/config/letsencrypt.conf: -------------------------------------------------------------------------------- 1 | # ACME-challenge 2 | location ^~ /.well-known/acme-challenge/ { 3 | root /var/www/_letsencrypt; 4 | } 5 | -------------------------------------------------------------------------------- /.ansible-deploy/roles/nginx/files/config/proxy.conf: -------------------------------------------------------------------------------- 1 | proxy_http_version 1.1; 2 | proxy_cache_bypass $http_upgrade; 3 | 4 | proxy_set_header Upgrade $http_upgrade; 5 | proxy_set_header Connection "upgrade"; 6 | proxy_set_header Host $host; 7 | proxy_set_header X-Real-IP $remote_addr; 8 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 9 | proxy_set_header X-Forwarded-Proto $scheme; 10 | proxy_set_header X-Forwarded-Host $host; 11 | proxy_set_header X-Forwarded-Port $server_port; 12 | -------------------------------------------------------------------------------- /.ansible-deploy/roles/nginx/files/config/security.conf: -------------------------------------------------------------------------------- 1 | # security headers 2 | add_header X-Frame-Options "SAMEORIGIN" always; 3 | add_header X-XSS-Protection "1; mode=block" always; 4 | add_header X-Content-Type-Options "nosniff" always; 5 | add_header Referrer-Policy "no-referrer-when-downgrade" always; 6 | add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always; 7 | add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; 8 | 9 | # . files 10 | location ~ /\.(?!well-known) { 11 | deny all; 12 | } 13 | -------------------------------------------------------------------------------- /.ansible-deploy/roles/nginx/files/nginx.conf: -------------------------------------------------------------------------------- 1 | # Generated by nginxconfig.io 2 | # https://www.digitalocean.com/community/tools/nginx#?0.domain=example.com&0.php=false&0.proxy&0.root=false&server_tokens&limit_req&brotli&expires_assets=14d&expires_media=14d&expires_svg=14d&expires_fonts=14d&client_max_body_size=25 3 | 4 | user www-data; 5 | pid /run/nginx.pid; 6 | worker_processes auto; 7 | worker_rlimit_nofile 65535; 8 | 9 | events { 10 | multi_accept on; 11 | worker_connections 65535; 12 | } 13 | 14 | http { 15 | charset utf-8; 16 | sendfile on; 17 | tcp_nopush on; 18 | tcp_nodelay on; 19 | log_not_found off; 20 | types_hash_max_size 2048; 21 | client_max_body_size 25M; 22 | 23 | # MIME 24 | include mime.types; 25 | default_type application/octet-stream; 26 | 27 | # logging 28 | access_log /var/log/nginx/access.log; 29 | error_log /var/log/nginx/error.log warn; 30 | 31 | # limits 32 | limit_req_log_level warn; 33 | limit_req_zone $binary_remote_addr zone=one:10m rate=30r/m; 34 | 35 | # SSL 36 | ssl_session_timeout 1d; 37 | ssl_session_cache shared:SSL:10m; 38 | ssl_session_tickets off; 39 | 40 | # Diffie-Hellman parameter for DHE ciphersuites 41 | ssl_dhparam /etc/nginx/dhparam.pem; 42 | 43 | # Mozilla Intermediate configuration 44 | ssl_protocols TLSv1.2 TLSv1.3; 45 | ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; 46 | 47 | # OCSP Stapling 48 | ssl_stapling on; 49 | ssl_stapling_verify on; 50 | resolver 1.1.1.1 1.0.0.1 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 valid=60s; 51 | resolver_timeout 2s; 52 | 53 | # load configs 54 | include /etc/nginx/conf.d/*.conf; 55 | include /etc/nginx/sites-enabled/*; 56 | } 57 | -------------------------------------------------------------------------------- /.ansible-deploy/roles/nginx/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Restart nginx 3 | service: 4 | name: nginx 5 | state: restarted 6 | 7 | - name: Reload nginx 8 | service: 9 | name: nginx 10 | state: reloaded -------------------------------------------------------------------------------- /.ansible-deploy/roles/nginx/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Add nginx repo 3 | apt_repository: 4 | repo: ppa:nginx/stable 5 | 6 | - name: Install nginx 7 | apt: 8 | name: nginx 9 | state: present 10 | force: yes 11 | update_cache: yes 12 | 13 | - name: Copy nginx config files 14 | copy: 15 | src: "{{ item }}" 16 | dest: /etc/nginx 17 | owner: "{{ deploy_user }}" 18 | group: "{{ deploy_group }}" 19 | with_items: 20 | - nginx.conf 21 | - config 22 | 23 | - name: Generate DH param (2048 bits) 24 | openssl_dhparam: 25 | path: /etc/nginx/dhparam.pem 26 | size: 2048 27 | when: nginx_https_enabled == true 28 | 29 | - name: Create a directory if it does not exist 30 | file: 31 | path: "/etc/nginx/{{ item }}" 32 | state: directory 33 | owner: "{{ deploy_user }}" 34 | group: "{{ deploy_group }}" 35 | mode: 0644 36 | with_items: 37 | - sites-available 38 | - sites-enabled 39 | 40 | - name: Copy nginx configuration in place 41 | template: 42 | src: "{{ nginx_conf_template }}" 43 | dest: "/etc/nginx/sites-available/default" 44 | owner: "{{ deploy_user }}" 45 | group: "{{ deploy_group }}" 46 | mode: 0644 47 | 48 | - name: Symlink default site 49 | file: 50 | src: /etc/nginx/sites-available/default 51 | dest: /etc/nginx/sites-enabled/default 52 | state: link 53 | 54 | - name: Set nginx user 55 | lineinfile: 56 | dest: /etc/nginx/nginx.conf 57 | regexp: "^user" 58 | line: "user {{ deploy_user }};" 59 | state: present 60 | 61 | # No need to restart nginx nginx_https_enabled is set to true 62 | # because certbot will install the certificates and then restart nginx 63 | - name: Restart nginx (when not running certbot) 64 | command: "/bin/true" 65 | notify: 66 | - Reload nginx 67 | - Restart nginx 68 | when: nginx_https_enabled != true -------------------------------------------------------------------------------- /.ansible-deploy/roles/nginx/templates/nginx-default.conf.j2: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | listen [::]:80; 4 | 5 | server_name {{ inventory_hostname }}; 6 | 7 | location / { 8 | return 200 "ok"; 9 | add_header Content-Type text/plain; 10 | } 11 | } -------------------------------------------------------------------------------- /.ansible-deploy/roles/nodejs/defaults/main.yml: -------------------------------------------------------------------------------- 1 | nodejs_version: "12.x" 2 | -------------------------------------------------------------------------------- /.ansible-deploy/roles/nodejs/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install dependencies 3 | apt: 4 | name: 5 | - apt-transport-https 6 | - gnupg2 7 | state: present 8 | 9 | - name: Add Nodesource apt key 10 | apt_key: 11 | url: https://keyserver.ubuntu.com/pks/lookup?op=get&fingerprint=on&search=0x1655A0AB68576280 12 | id: "68576280" 13 | state: present 14 | 15 | - name: Add NodeSource repositories 16 | apt_repository: 17 | repo: "{{ item }}" 18 | state: present 19 | with_items: 20 | - "deb https://deb.nodesource.com/node_{{ nodejs_version }} {{ ansible_distribution_release }} main" 21 | - "deb-src https://deb.nodesource.com/node_{{ nodejs_version }} {{ ansible_distribution_release }} main" 22 | register: node_repo 23 | 24 | - name: Update apt cache if repo was added 25 | apt: update_cache=yes 26 | when: node_repo.changed 27 | 28 | - name: Ensure Node.js and npm are installed 29 | apt: 30 | name: "nodejs={{ nodejs_version|regex_replace('x', '') }}*" 31 | state: present -------------------------------------------------------------------------------- /.ansible-deploy/roles/pgbackup/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | postgresql_backup_dir: "backups" 3 | postgresql_backup_filename_format: >- 4 | {{ app_name }}-%Y%m%d-%H%M%S.pgdump 5 | postgresql_db_backup_healthcheck: "" 6 | postgresql_s3_backup_hour: "3" 7 | postgresql_s3_backup_minute: "*" 8 | postgresql_s3_backup_delete_after: "7 days" -------------------------------------------------------------------------------- /.ansible-deploy/roles/pgbackup/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Create postgresql backup directory 2 | file: 3 | path: "{{ postgresql_backup_dir }}" 4 | recurse: true 5 | state: directory 6 | 7 | - name: Set backup directory permissions 8 | file: 9 | path: "{{ postgresql_backup_dir }}" 10 | state: directory 11 | owner: "{{ deploy_user }}" 12 | group: "{{ deploy_group }}" 13 | mode: 0700 14 | 15 | - name: Upload backup script 16 | become: true 17 | template: 18 | src: postgresql-backup.j2 19 | dest: "{{ postgresql_backup_dir }}/postgresql-backup.sh" 20 | mode: 0755 21 | 22 | - name: Configure backup cron job 23 | cron: 24 | name: Backup cron job 25 | minute: "{{ postgresql_s3_backup_minute }}" 26 | hour: "{{ postgresql_s3_backup_hour }}" 27 | user: "{{ deploy_user }}" 28 | job: "bash -lc {{ postgresql_backup_dir }}/postgresql-backup.sh" 29 | cron_file: "postgresql-backup" 30 | state: present 31 | 32 | -------------------------------------------------------------------------------- /.ansible-deploy/roles/pgbackup/templates/postgresql-backup.j2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 5 | 6 | NOW=$(date +"%Y-%m-%d-at-%H-%M-%S") 7 | FILENAME="{{ postgresql_db_name }}"-"$NOW" 8 | 9 | DELETION_TIMESTAMP=`[ "$(uname)" = Linux ] && date +%s --date="-{{ postgresql_s3_backup_delete_after }}"` 10 | 11 | echo " * Generating backup"; 12 | PGPASSWORD={{ postgresql_db_password }} pg_dump -Fc --no-acl --no-owner -h localhost -U {{ postgresql_db_user }} {{ postgresql_db_name }} > {{ postgresql_backup_dir }}/"$FILENAME".dump 13 | 14 | echo " * Uploading to S3"; 15 | aws s3 cp {{ postgresql_backup_dir }}/"$FILENAME".dump s3://{{ postgresql_s3_backup_bucket }}/"$FILENAME".dump 16 | 17 | echo " * Delete local file"; 18 | # rm {{ postgresql_backup_dir }}/"$FILENAME".dump 19 | 20 | 21 | # Delete old files 22 | echo " * Deleting old backups..."; 23 | 24 | # Loop through files 25 | aws s3 ls s3://{{ postgresql_s3_backup_bucket }}/ | while read -r line; do 26 | # Get file creation date 27 | createDate=`echo $line|awk {'print $1" "$2'}` 28 | createDate=`date -d"$createDate" +%s` 29 | 30 | if [[ $createDate -lt $DELETION_TIMESTAMP ]] 31 | then 32 | # Get file name 33 | FILENAME=`echo $line|awk {'print $4'}` 34 | if [[ $FILENAME != "" ]] 35 | then 36 | echo " -> Deleting $FILENAME" 37 | aws s3 rm s3://{{ postgresql_s3_backup_bucket }}/$FILENAME 38 | fi 39 | fi 40 | done; 41 | 42 | echo " * Ping Healthcheck URL"; 43 | curl -fsS --retry 3 {{ postgresql_db_backup_healthcheck }} 44 | -------------------------------------------------------------------------------- /.ansible-deploy/roles/postgresql/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | postgresql_version: "9.6" 3 | 4 | postgresql_packages: 5 | - "postgresql-{{ postgresql_version }}" 6 | - "postgresql-contrib-{{ postgresql_version }}" 7 | 8 | postgresql_python_packages: 9 | - "python3-psycopg2" 10 | 11 | postgresql_apt_url: "https://apt.postgresql.org/pub/repos/apt" 12 | postgresql_apt_key: "{{ postgresql_apt_url }}/ACCC4CF8.asc" 13 | postgresql_apt_repo: "deb {{ postgresql_apt_url }}/ {{ ansible_lsb.codename }}-pgdg main" 14 | 15 | postgresql_config_path: /etc/postgresql/{{ postgresql_version }}/main 16 | postgresql_data_path: /var/lib/postgresql/{{ postgresql_version }}/main 17 | postgresql_pid_file: /var/run/postgresql/{{ postgresql_version }}-main.pid 18 | 19 | # default admin user 20 | postgresql_admin_user: postgres 21 | 22 | # table locale and character encoding 23 | postgresql_locale: "en_US" 24 | postgresql_encoding: "UTF-8" 25 | 26 | # shell locale and character encoding 27 | postgresql_shell_locale: "{{ postgresql_locale }}" 28 | postgresql_shell_encoding: "{{ postgresql_encoding | replace('-', '') | lower }}" 29 | 30 | pg_hba_template: "pg_hba.conf.j2" 31 | postgresql_parameters_template: "postgresql.conf.j2" 32 | 33 | # default application database 34 | postgresql_db_user: '' # name of the user (empty means no user is created) 35 | postgresql_db_password: '' 36 | postgresql_db_name: '' # name of the database (empty means no database is created) 37 | 38 | # all the created users 39 | postgresql_users: 40 | - name: "{{ postgresql_db_user | default('') }}" 41 | password: "{{ postgresql_db_password | default('') }}" # only needed if user is given 42 | role_attr_flags: "{{ postgresql_users_role_attr_flags }}" 43 | 44 | # all the created databases 45 | postgresql_databases: 46 | - name: "{{ postgresql_db_name }}" 47 | owner: "{{ postgresql_db_user | default('') }}" # empty mean 'postgres' user will own it 48 | # encoding: "{{ postgresql_encoding }}" 49 | # lc_collate: "{{ postgresql_locale }}.{{ postgresql_encoding }}" 50 | # lc_ctype: "{{ postgresql_locale }}.{{ postgresql_encoding }}" 51 | # template: template0 52 | 53 | # default user attr_flags 54 | postgresql_users_role_attr_flags: 55 | - CREATEDB 56 | - NOSUPERUSER 57 | 58 | 59 | # variables to build postgresql.conf 60 | # postgresql_host: "{{ hostvars[inventory_hostname]['ansible_default_ipv4']['address'] }}" 61 | postgresql_listen: 62 | - "localhost" 63 | 64 | postgresql_port: 5432 65 | postgresql_max_connections: 100 66 | 67 | postgresql_connections: 68 | ssl: false 69 | 70 | postgresql_resources: 71 | shared_buffers: 128MB 72 | 73 | postgresql_write_ahead_log: {} 74 | postgresql_replication: {} 75 | postgresql_query_tuning: {} 76 | postgresql_logging: 77 | log_line_prefix: "'%t '" 78 | log_timezone: "'UTC'" 79 | 80 | postgresql_runtime_statistics: {} 81 | postgresql_autovacuum: {} 82 | postgresql_client_connection_defaults: 83 | datestyle: "'iso, mdy'" 84 | timezone: "'UTC'" 85 | lc_messages: "'{{ postgresql_locale }}.{{ postgresql_encoding }}'" 86 | lc_monetary: "'{{ postgresql_locale }}.{{ postgresql_encoding }}'" 87 | lc_numeric: "'{{ postgresql_locale }}.{{ postgresql_encoding }}'" 88 | lc_time: "'{{ postgresql_locale }}.{{ postgresql_encoding }}'" 89 | default_text_search_config: "'pg_catalog.english'" 90 | 91 | postgresql_lock_management: {} 92 | postgresql_cutomized: {} 93 | 94 | postgresql_service: postgresql 95 | 96 | postgresql_socket_directories: 97 | - "/var/run/postgresql" -------------------------------------------------------------------------------- /.ansible-deploy/roles/postgresql/handlers/main.yml: -------------------------------------------------------------------------------- 1 | - name: Restart postgresql 2 | service: 3 | name: "{{ postgresql_service }}" 4 | state: restarted -------------------------------------------------------------------------------- /.ansible-deploy/roles/postgresql/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Set shell locales 3 | copy: 4 | dest: /etc/profile.d/lang.sh 5 | content: | 6 | export LANGUAGE="{{ postgresql_locale }}.{{ postgresql_shell_encoding }}" 7 | export LANG="{{ postgresql_locale }}.{{ postgresql_shell_encoding }}" 8 | export LC_ALL="{{ postgresql_locale }}.{{ postgresql_shell_encoding }}" 9 | 10 | - name: Add postgres repo key 11 | apt_key: 12 | url: "{{ postgresql_apt_key }}" 13 | 14 | - name: Add postgres repo 15 | apt_repository: 16 | repo: "{{ postgresql_apt_repo }}" 17 | 18 | - name: Install required postgres packages 19 | apt: name={{ item }} state=latest update_cache=yes cache_valid_time=86400 20 | loop: 21 | - "{{ postgresql_packages }}" 22 | - "{{ postgresql_python_packages }}" 23 | - "postgresql-{{ postgresql_version }}" 24 | - "postgresql-contrib-{{ postgresql_version }}" 25 | 26 | - name: Configure pg_hba.conf 27 | template: 28 | src: "{{ pg_hba_template }}" 29 | dest: "{{ postgresql_config_path }}/pg_hba.conf" 30 | 31 | - name: Configure postgresql.conf 32 | template: 33 | src: "{{ postgresql_parameters_template }}" 34 | dest: "{{ postgresql_config_path }}/postgresql.conf" 35 | notify: 36 | - Restart postgresql 37 | 38 | - meta: flush_handlers 39 | 40 | - name: Template locales 41 | shell: > 42 | psql -c "update pg_database 43 | set 44 | encoding = pg_char_to_encoding('{{ postgresql_encoding }}'), 45 | datctype = '{{ postgresql_locale }}.{{ postgresql_encoding }}', 46 | datcollate = '{{ postgresql_locale }}.{{ postgresql_encoding }}' 47 | where 48 | encoding != pg_char_to_encoding('{{ postgresql_encoding }}') 49 | or datctype != '{{ postgresql_locale }}.{{ postgresql_encoding }}' 50 | or datcollate != '{{ postgresql_locale }}.{{ postgresql_encoding }}';" 51 | register: postgresql_update_template_result 52 | changed_when: > 53 | postgresql_update_template_result.stdout is defined 54 | and 'UPDATE 0' != postgresql_update_template_result.stdout 55 | ignore_errors: yes 56 | become: yes 57 | become_user: "{{ postgresql_admin_user }}" 58 | 59 | - name: Create users 60 | postgresql_user: 61 | name: "{{ item.name }}" 62 | password: "{{ item.password }}" 63 | role_attr_flags: "{{ item.role_attr_flags | default(postgresql_users_role_attr_flags) | join(',') | replace('\\n', '') }}" 64 | when: item.name != '' 65 | with_items: "{{ postgresql_users }}" 66 | become: yes 67 | become_user: "{{ postgresql_admin_user }}" 68 | 69 | - name: Create databases 70 | postgresql_db: 71 | name: "{{ item.name }}" 72 | owner: "{{ item.owner | default(postgresql_user, true) }}" 73 | encoding: "{{ item.encoding | default(postgresql_encoding) }}" 74 | lc_collate: "{{ item.lc_collate | default(postgresql_locale + '.' + postgresql_encoding) }}" 75 | lc_ctype: "{{ item.lc_ctype | default(postgresql_locale + '.' + postgresql_encoding) }}" 76 | template: "{{ item.template | default('template0') }}" 77 | state: present 78 | when: item.name != '' 79 | with_items: "{{ postgresql_databases }}" 80 | become: yes 81 | become_user: "{{ postgresql_admin_user }}" 82 | -------------------------------------------------------------------------------- /.ansible-deploy/roles/postgresql/templates/pg_hba.conf.j2: -------------------------------------------------------------------------------- 1 | local all postgres peer 2 | local all all peer 3 | host all all 127.0.0.1/32 md5 4 | host all all ::1/128 md5 -------------------------------------------------------------------------------- /.ansible-deploy/roles/postgresql/templates/postgresql.conf.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | 3 | # ----------------------------- 4 | # PostgreSQL configuration file 5 | # ----------------------------- 6 | 7 | #------------------------------------------------------------------------------ 8 | # FILE LOCATIONS 9 | #------------------------------------------------------------------------------ 10 | 11 | data_directory = '{{ postgresql_data_path }}' 12 | hba_file = '{{ postgresql_config_path }}/pg_hba.conf' 13 | ident_file = '{{ postgresql_config_path }}/pg_ident.conf' 14 | external_pid_file = '{{ postgresql_pid_file }}' 15 | 16 | #------------------------------------------------------------------------------ 17 | # CONNECTIONS AND AUTHENTICATION 18 | #------------------------------------------------------------------------------ 19 | 20 | listen_addresses = '{{ postgresql_listen | join(',') }}' 21 | port = {{ postgresql_port }} 22 | max_connections = {{ postgresql_max_connections }} 23 | unix_socket_directories = '{{ postgresql_socket_directories | join(',') }}' 24 | {% for k,v in postgresql_connections.items() | list %} 25 | {{ k }} = {{ v }} 26 | {% endfor %} 27 | 28 | #------------------------------------------------------------------------------ 29 | # RESOURCE USAGE (except WAL) 30 | #------------------------------------------------------------------------------ 31 | 32 | {% for k,v in postgresql_resources.items() | list %} 33 | {{ k }} = {{ v }} 34 | {% endfor %} 35 | 36 | #------------------------------------------------------------------------------ 37 | # WRITE AHEAD LOG 38 | #------------------------------------------------------------------------------ 39 | 40 | {% for k,v in postgresql_write_ahead_log.items() | list %} 41 | {{ k }} = {{ v }} 42 | {% endfor %} 43 | 44 | #------------------------------------------------------------------------------ 45 | # REPLICATION 46 | #------------------------------------------------------------------------------ 47 | 48 | {% for k,v in postgresql_replication.items() | list %} 49 | {{ k }} = {{ v }} 50 | {% endfor %} 51 | 52 | #------------------------------------------------------------------------------ 53 | # QUERY TUNING 54 | #------------------------------------------------------------------------------ 55 | 56 | {% for k,v in postgresql_query_tuning.items() | list %} 57 | {{ k }} = {{ v }} 58 | {% endfor %} 59 | 60 | #------------------------------------------------------------------------------ 61 | # ERROR REPORTING AND LOGGING 62 | #------------------------------------------------------------------------------ 63 | 64 | {% for k,v in postgresql_logging.items() | list %} 65 | {{ k }} = {{ v }} 66 | {% endfor %} 67 | 68 | #------------------------------------------------------------------------------ 69 | # RUNTIME STATISTICS 70 | #------------------------------------------------------------------------------ 71 | 72 | {% for k,v in postgresql_runtime_statistics.items() | list %} 73 | {{ k }} = {{ v }} 74 | {% endfor %} 75 | 76 | #------------------------------------------------------------------------------ 77 | # AUTOVACUUM PARAMETERS 78 | #------------------------------------------------------------------------------ 79 | 80 | {% for k,v in postgresql_autovacuum.items() | list %} 81 | {{ k }} = {{ v }} 82 | {% endfor %} 83 | 84 | #------------------------------------------------------------------------------ 85 | # CLIENT CONNECTION DEFAULTS 86 | #------------------------------------------------------------------------------ 87 | 88 | {% for k,v in postgresql_client_connection_defaults.items() | list %} 89 | {{ k }} = {{ v }} 90 | {% endfor %} 91 | 92 | #------------------------------------------------------------------------------ 93 | # LOCK MANAGEMENT 94 | #------------------------------------------------------------------------------ 95 | 96 | {% for k,v in postgresql_lock_management.items() | list %} 97 | {{ k }} = {{ v }} 98 | {% endfor %} 99 | 100 | #------------------------------------------------------------------------------ 101 | # CUSTOMIZED OPTIONS 102 | #------------------------------------------------------------------------------ 103 | 104 | {% for k,v in postgresql_cutomized.items() | list %} 105 | {{ k }} = {{ v }} 106 | {% endfor %} -------------------------------------------------------------------------------- /.ansible-deploy/roles/puma/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Copy puma.service 3 | template: 4 | src: "{{ puma_service_file }}" 5 | dest: /lib/systemd/system/puma.service 6 | force: yes 7 | owner: "{{ deploy_user }}" 8 | group: "{{ deploy_group }}" 9 | mode: 0644 10 | register: puma_service_file 11 | 12 | - name: Ensure that we re-read puma.service 13 | systemd: 14 | daemon_reload: yes 15 | name: "puma" 16 | when: puma_service_file.changed 17 | 18 | - name: Enable puma 19 | service: 20 | name: puma 21 | enabled: yes 22 | 23 | - name: Restart puma 24 | service: 25 | name: puma 26 | state: restarted 27 | -------------------------------------------------------------------------------- /.ansible-deploy/roles/redis/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install Redis Server 3 | apt: name=redis-server state=latest 4 | 5 | - name: Install Redis Tools 6 | apt: name=redis-tools state=latest 7 | 8 | - name: Ensure Redis Server is running 9 | service: name=redis-server state=started enabled=yes -------------------------------------------------------------------------------- /.ansible-deploy/roles/ruby/defaults/main.yml: -------------------------------------------------------------------------------- 1 | ruby_version: 2.7.2 2 | additional_rubies: [] -------------------------------------------------------------------------------- /.ansible-deploy/roles/ruby/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install rbenv 3 | become: yes 4 | become_user: "{{ deploy_user }}" 5 | git: 6 | repo: "https://github.com/rbenv/rbenv.git" 7 | dest: "{{ rbenv_root_path }}" 8 | depth: 1 9 | accept_hostkey: yes 10 | clone: yes 11 | update: yes 12 | 13 | - name: Install ruby-build 14 | become: yes 15 | become_user: "{{ deploy_user }}" 16 | git: 17 | repo: "https://github.com/rbenv/ruby-build.git" 18 | dest: "{{ rbenv_root_path }}/plugins/ruby-build" 19 | depth: 1 20 | 21 | - name: Install rbenv-vars 22 | become: yes 23 | become_user: "{{ deploy_user }}" 24 | git: 25 | repo: "https://github.com/rbenv/rbenv-vars.git" 26 | dest: "{{ rbenv_root_path }}/plugins/rbenv-vars" 27 | depth: 1 28 | 29 | - name: Ensure {{ rbenv_shell_rc }} exists 30 | become: true 31 | become_user: "{{ deploy_user }}" 32 | shell: "touch {{ rbenv_shell_rc_path }}" 33 | args: 34 | creates: "{{ rbenv_shell_rc_path }}" 35 | 36 | - name: Export RBENV_ROOT in {{ rbenv_shell_rc_path }} 37 | become: true 38 | become_user: "{{ deploy_user }}" 39 | lineinfile: 40 | dest: "{{ rbenv_shell_rc_path }}" 41 | regexp: "^export RBENV_ROOT=" 42 | line: "export RBENV_ROOT={{ rbenv_root_path }}" 43 | 44 | - name: Put rbenv in users PATH in {{ rbenv_shell_rc_path }} 45 | become: true 46 | become_user: "{{ deploy_user }}" 47 | lineinfile: 48 | dest: "{{ rbenv_shell_rc_path }}" 49 | regexp: "^PATH=\\$PATH:\\$RBENV_ROOT/bin" 50 | line: "PATH=$RBENV_ROOT/bin:$PATH" 51 | 52 | - name: Put $RBENV_ROOT/shims in users $PATH in {{ rbenv_shell_rc_path }} 53 | become: true 54 | become_user: "{{ deploy_user }}" 55 | lineinfile: 56 | dest: "{{ rbenv_shell_rc_path }}" 57 | regexp: "^PATH=\\$RBENV_ROOT/shims:\\$PATH" 58 | line: "PATH=$RBENV_ROOT/shims:$PATH" 59 | 60 | - name: Install Rubies 61 | become: yes 62 | become_user: "{{ deploy_user }}" 63 | shell: "{{ rbenv_ruby_configure_opts | default('') }} {{ rbenv_bin }} install {{ item }}" 64 | args: 65 | creates: "{{ rbenv_root_path }}/versions/{{ item }}" 66 | with_flattened: 67 | - "{{ additional_rubies }}" 68 | - "{{ ruby_version }}" 69 | 70 | 71 | - name: Check default ruby 72 | shell: '{{ rbenv_bin }} version | grep -oE "^[^ ]+"' 73 | changed_when: no 74 | register: rbenv_current_version 75 | become: yes 76 | become_user: "{{ deploy_user }}" 77 | 78 | - name: Set default ruby 79 | shell: "{{ rbenv_bin }} global {{ ruby_version }}" 80 | become: yes 81 | become_user: "{{ deploy_user }}" 82 | when: rbenv_current_version.stdout != ruby_version 83 | 84 | - name: Install Bundler 85 | shell: "{{ rbenv_root_path }}/shims/gem install bundler" 86 | become: yes 87 | become_user: "{{ deploy_user }}" 88 | -------------------------------------------------------------------------------- /.ansible-deploy/roles/sidekiq/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Copy sidekiq.service 3 | template: 4 | src: "{{ sidekiq_service_file }}" 5 | dest: /lib/systemd/system/sidekiq.service 6 | force: yes 7 | owner: "{{ deploy_user }}" 8 | group: "{{ deploy_group }}" 9 | mode: 0644 10 | register: sidekiq_service_file 11 | 12 | - name: Ensure that we re-read sidekiq.service 13 | systemd: 14 | daemon_reload: yes 15 | name: "sidekiq" 16 | when: sidekiq_service_file.changed 17 | 18 | - name: Enable sidekiq 19 | service: 20 | name: sidekiq 21 | enabled: yes 22 | 23 | - name: Restart sidekiq 24 | service: 25 | name: sidekiq 26 | state: restarted 27 | -------------------------------------------------------------------------------- /.ansible-deploy/roles/ssh/defaults/main.yml: -------------------------------------------------------------------------------- 1 | ssh_port: 22 2 | ssh_password_authentication: "no" 3 | ssh_permit_root_login: "no" 4 | ssh_usedns: "no" 5 | ssh_permit_empty_password: "no" 6 | ssh_challenge_response_auth: "no" 7 | ssh_gss_api_authentication: "no" 8 | ssh_x11_forwarding: "no" -------------------------------------------------------------------------------- /.ansible-deploy/roles/ssh/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Restart SSH 3 | service: 4 | name: ssh 5 | state: restarted -------------------------------------------------------------------------------- /.ansible-deploy/roles/ssh/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Update SSH configuration to be more secure 3 | lineinfile: 4 | dest: "/etc/ssh/sshd_config" 5 | regexp: "{{ item.regexp }}" 6 | line: "{{ item.line }}" 7 | state: present 8 | with_items: 9 | - regexp: "^PasswordAuthentication" 10 | line: "PasswordAuthentication {{ ssh_password_authentication }}" 11 | - regexp: "^PermitRootLogin" 12 | line: "PermitRootLogin {{ ssh_permit_root_login }}" 13 | - regexp: "^Port" 14 | line: "Port {{ ssh_port }}" 15 | - regexp: "^UseDNS" 16 | line: "UseDNS {{ ssh_usedns }}" 17 | - regexp: "^PermitEmptyPasswords" 18 | line: "PermitEmptyPasswords {{ ssh_permit_empty_password }}" 19 | - regexp: "^ChallengeResponseAuthentication" 20 | line: "ChallengeResponseAuthentication {{ ssh_challenge_response_auth }}" 21 | - regexp: "^GSSAPIAuthentication" 22 | line: "GSSAPIAuthentication {{ ssh_gss_api_authentication }}" 23 | - regexp: "^X11Forwarding" 24 | line: "X11Forwarding {{ ssh_x11_forwarding }}" 25 | notify: Restart SSH -------------------------------------------------------------------------------- /.ansible-deploy/roles/ufw/defaults/main.yml: -------------------------------------------------------------------------------- 1 | ufw_rules: [] -------------------------------------------------------------------------------- /.ansible-deploy/roles/ufw/handlers/main.yml: -------------------------------------------------------------------------------- 1 | - name: Restart UFW 2 | service: name=ufw state=restarted -------------------------------------------------------------------------------- /.ansible-deploy/roles/ufw/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Install UFW 2 | apt: package=ufw state=present 3 | 4 | - name: Reset UFW to defaults 5 | ufw: state=reset 6 | 7 | - name: Configure UFW defaults 8 | ufw: direction={{ item.direction }} policy={{ item.policy }} 9 | with_items: 10 | - { direction: 'incoming', policy: 'deny' } 11 | - { direction: 'outgoing', policy: 'allow' } 12 | notify: 13 | - Restart UFW 14 | 15 | - name: Configure UFW rules 16 | ufw: rule={{ item.rule }} port={{ item.port }} from={{ item.from }} proto={{ item.proto }} 17 | with_items: 18 | - { rule: 'limit', port: '{{ ssh_port | default("22") }}', proto: 'tcp', from: "any" } 19 | - "{{ ufw_rules }}" 20 | notify: 21 | - Restart UFW 22 | 23 | - name: Enable UFW logging 24 | ufw: logging=on 25 | notify: 26 | - Restart UFW 27 | 28 | - name: Enable UFW 29 | ufw: state=enabled -------------------------------------------------------------------------------- /.ansible-deploy/roles/user/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | deploy_group: "{{ deploy_group }}" 3 | deploy_user: "{{ deploy_user }}" 4 | copy_local_key: "{{ lookup('file', lookup('env','HOME') + '/.ssh/id_rsa.pub') }}" -------------------------------------------------------------------------------- /.ansible-deploy/roles/user/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Ensure sudo group is present 3 | group: 4 | name: sudo 5 | state: present 6 | 7 | - name: Ensure sudo group has sudo privileges 8 | lineinfile: 9 | dest: /etc/sudoers 10 | state: present 11 | regexp: "^%sudo" 12 | line: "%sudo ALL=(ALL:ALL) ALL" 13 | validate: "/usr/sbin/visudo -cf %s" 14 | 15 | - name: Make sure we have a deployment group 16 | group: 17 | name: "{{ deploy_group }}" 18 | state: present 19 | 20 | - name: Allow deployment group to have passwordless sudo 21 | lineinfile: 22 | path: /etc/sudoers 23 | state: present 24 | regexp: '^%{{ deploy_group }}' 25 | line: '%{{ deploy_group }} ALL=(ALL) NOPASSWD: ALL' 26 | validate: '/usr/sbin/visudo -cf %s' 27 | 28 | - name: Create a new user with sudo privileges 29 | user: 30 | name: "{{ deploy_user }}" 31 | state: present 32 | groups: "{{ deploy_group }}" 33 | append: true 34 | create_home: true 35 | shell: /bin/bash 36 | 37 | - name: Set authorized key for remote user 38 | authorized_key: 39 | user: "{{ deploy_user }}" 40 | state: present 41 | key: "{{ copy_local_key }}" -------------------------------------------------------------------------------- /.ansible-deploy/roles/yarn/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Add Yarn apt key 3 | apt_key: 4 | url: https://dl.yarnpkg.com/debian/pubkey.gpg 5 | 6 | - name: Add Yarn repository 7 | apt_repository: 8 | repo: "deb https://dl.yarnpkg.com/debian/ stable main" 9 | filename: yarn 10 | 11 | - name: Install Yarn 12 | apt: 13 | name: yarn -------------------------------------------------------------------------------- /.ansible-deploy/templates/.rbenv-vars.j2: -------------------------------------------------------------------------------- 1 | RAILS_ENV=production 2 | RACK_ENV=production 3 | RAILS_MASTER_KEY={{ vault_rails_master_key }} 4 | DB_POOL={{ rails_db_pool }} 5 | 6 | # Puma 7 | APP_DIR={{ app_current_path }} 8 | SHARED_DIR={{ app_shared_path }} 9 | WEB_CONCURRENCY={{ puma_web_concurrency }} -------------------------------------------------------------------------------- /.ansible-deploy/templates/database.yml.j2: -------------------------------------------------------------------------------- 1 | production: 2 | adapter: postgresql 3 | encoding: unicode 4 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 5 | database: "{{ postgresql_db_name }}" 6 | username: "{{ postgresql_db_user }}" 7 | password: "{{ postgresql_db_password }}" 8 | host: "localhost" -------------------------------------------------------------------------------- /.ansible-deploy/templates/nginx.conf.j2: -------------------------------------------------------------------------------- 1 | upstream app { 2 | # Path to Puma SOCK file, as defined previously 3 | server unix:/{{ puma_socket }} fail_timeout=0; 4 | } 5 | 6 | server { 7 | listen 80; 8 | server_name demo-ansible.pedroalonso.net; 9 | 10 | root {{ app_current_path }}/public; 11 | 12 | try_files $uri/index.html $uri @app; 13 | 14 | location /sidekiq { 15 | proxy_set_header X-Script-Name http://app/sidekiq; 16 | proxy_pass http://app/sidekiq; 17 | 18 | proxy_set_header Host $host; 19 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 20 | proxy_set_header X-Scheme $scheme; 21 | proxy_set_header X-Real-IP $remote_addr; 22 | proxy_redirect off; 23 | } 24 | 25 | location @app { 26 | proxy_pass http://app; 27 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 28 | proxy_set_header Host $http_host; 29 | proxy_set_header X-Real-IP $remote_addr; 30 | proxy_redirect off; 31 | } 32 | 33 | error_page 500 502 503 504 /500.html; 34 | client_max_body_size 4G; 35 | keepalive_timeout 10; 36 | } -------------------------------------------------------------------------------- /.ansible-deploy/templates/puma.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Puma HTTP Server 3 | After=network.target 4 | 5 | [Service] 6 | Type=simple 7 | User={{ deploy_user }} 8 | EnvironmentFile={{ app_root_path }}/.rbenv-vars 9 | 10 | WorkingDirectory={{ app_current_path }} 11 | 12 | ExecStart={{ rbenv_bundle }} exec puma -C {{ app_current_path }}/config/puma.rb 13 | ExecStop={{ rbenv_bundle }} exec puma -S {{ app_current_path }}/config/puma.rb 14 | PIDFile={{ app_pids_path }}/puma.pid 15 | 16 | # Should systemd restart puma? 17 | # Use "no" (the default) to ensure no interference when using 18 | # stop/start/restart via `pumactl`. The "on-failure" setting might 19 | # work better for this purpose, but you must test it. 20 | # Use "always" if only `systemctl` is used for start/stop/restart, and 21 | # reconsider if you actually need the forking config. 22 | Restart=always 23 | 24 | [Install] 25 | WantedBy=multi-user.target 26 | 27 | -------------------------------------------------------------------------------- /.ansible-deploy/templates/sidekiq.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Sidekiq 3 | After=network.target 4 | 5 | [Service] 6 | Type=notify 7 | User={{ deploy_user }} 8 | 9 | EnvironmentFile={{ app_root_path }}/.rbenv-vars 10 | 11 | WorkingDirectory={{ app_current_path }} 12 | 13 | ExecStart={{ rbenv_bundle }} exec sidekiq -e production -C config/sidekiq.yml 14 | 15 | # Greatly reduce Ruby memory fragmentation and heap usage 16 | # https://www.mikeperham.com/2018/04/25/taming-rails-memory-bloat/ 17 | Environment=MALLOC_ARENA_MAX=2 18 | 19 | # if we crash, restart 20 | RestartSec=1 21 | Restart=on-failure 22 | 23 | [Install] 24 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | defaults 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # See https://git-scm.com/docs/gitattributes for more about git attribute files. 2 | 3 | # Mark the database schema as having been generated. 4 | db/schema.rb linguist-generated 5 | 6 | # Mark the yarn lockfile as having been generated. 7 | yarn.lock linguist-generated 8 | 9 | # Mark any vendored files as having been vendored. 10 | vendor/* linguist-vendored 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | /db/*.sqlite3 12 | /db/*.sqlite3-* 13 | 14 | # Ignore all logfiles and tempfiles. 15 | /log/* 16 | /tmp/* 17 | !/log/.keep 18 | !/tmp/.keep 19 | 20 | # Ignore pidfiles, but keep the directory. 21 | /tmp/pids/* 22 | !/tmp/pids/ 23 | !/tmp/pids/.keep 24 | 25 | # Ignore uploaded files in development. 26 | /storage/* 27 | !/storage/.keep 28 | 29 | /public/assets 30 | .byebug_history 31 | 32 | # Ignore master key for decrypting credentials and more. 33 | /config/master.key 34 | 35 | /public/packs 36 | /public/packs-test 37 | /node_modules 38 | /yarn-error.log 39 | yarn-debug.log* 40 | .yarn-integrity 41 | 42 | # Ignore Ansible secrets 43 | .ansible-deploy/.vault_pass 44 | .ansible-deploy/group_vars/all/vault.yml 45 | 46 | .DS_Store 47 | .ansible-deploy/.DS_Store -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.7.2 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 3 | 4 | ruby '2.7.2' 5 | 6 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' 7 | gem 'rails', '~> 6.1.0' 8 | # Use sqlite3 as the database for Active Record 9 | gem 'sqlite3', '~> 1.4' 10 | # Use Puma as the app server 11 | gem 'puma', '~> 5.0' 12 | # Use SCSS for stylesheets 13 | gem 'sass-rails', '>= 6' 14 | # Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker 15 | gem 'webpacker', '~> 5.0' 16 | # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks 17 | gem 'turbolinks', '~> 5' 18 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder 19 | gem 'jbuilder', '~> 2.7' 20 | # Use Redis adapter to run Action Cable in production 21 | # gem 'redis', '~> 4.0' 22 | # Use Active Model has_secure_password 23 | # gem 'bcrypt', '~> 3.1.7' 24 | gem 'sidekiq' 25 | 26 | # Use Active Storage variant 27 | # gem 'image_processing', '~> 1.2' 28 | 29 | # Reduces boot times through caching; required in config/boot.rb 30 | # gem 'bootsnap', require: false 31 | 32 | group :development, :test do 33 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console 34 | gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] 35 | end 36 | 37 | group :development do 38 | # Access an interactive console on exception pages or by calling 'console' anywhere in the code. 39 | gem 'web-console', '>= 4.1.0' 40 | # Display performance information such as SQL time and flame graphs for each request in your browser. 41 | # Can be configured to work on production as well see: https://github.com/MiniProfiler/rack-mini-profiler/blob/master/README.md 42 | gem 'rack-mini-profiler', '~> 2.0' 43 | gem 'listen', '~> 3.3' 44 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring 45 | gem 'spring' 46 | end 47 | 48 | group :test do 49 | # Adds support for Capybara system testing and selenium driver 50 | gem 'capybara', '>= 3.26' 51 | gem 'selenium-webdriver' 52 | # Easy installation and use of web drivers to run system tests with browsers 53 | gem 'webdrivers' 54 | end 55 | 56 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 57 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] 58 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | actioncable (6.1.1) 5 | actionpack (= 6.1.1) 6 | activesupport (= 6.1.1) 7 | nio4r (~> 2.0) 8 | websocket-driver (>= 0.6.1) 9 | actionmailbox (6.1.1) 10 | actionpack (= 6.1.1) 11 | activejob (= 6.1.1) 12 | activerecord (= 6.1.1) 13 | activestorage (= 6.1.1) 14 | activesupport (= 6.1.1) 15 | mail (>= 2.7.1) 16 | actionmailer (6.1.1) 17 | actionpack (= 6.1.1) 18 | actionview (= 6.1.1) 19 | activejob (= 6.1.1) 20 | activesupport (= 6.1.1) 21 | mail (~> 2.5, >= 2.5.4) 22 | rails-dom-testing (~> 2.0) 23 | actionpack (6.1.1) 24 | actionview (= 6.1.1) 25 | activesupport (= 6.1.1) 26 | rack (~> 2.0, >= 2.0.9) 27 | rack-test (>= 0.6.3) 28 | rails-dom-testing (~> 2.0) 29 | rails-html-sanitizer (~> 1.0, >= 1.2.0) 30 | actiontext (6.1.1) 31 | actionpack (= 6.1.1) 32 | activerecord (= 6.1.1) 33 | activestorage (= 6.1.1) 34 | activesupport (= 6.1.1) 35 | nokogiri (>= 1.8.5) 36 | actionview (6.1.1) 37 | activesupport (= 6.1.1) 38 | builder (~> 3.1) 39 | erubi (~> 1.4) 40 | rails-dom-testing (~> 2.0) 41 | rails-html-sanitizer (~> 1.1, >= 1.2.0) 42 | activejob (6.1.1) 43 | activesupport (= 6.1.1) 44 | globalid (>= 0.3.6) 45 | activemodel (6.1.1) 46 | activesupport (= 6.1.1) 47 | activerecord (6.1.1) 48 | activemodel (= 6.1.1) 49 | activesupport (= 6.1.1) 50 | activestorage (6.1.1) 51 | actionpack (= 6.1.1) 52 | activejob (= 6.1.1) 53 | activerecord (= 6.1.1) 54 | activesupport (= 6.1.1) 55 | marcel (~> 0.3.1) 56 | mimemagic (~> 0.3.2) 57 | activesupport (6.1.1) 58 | concurrent-ruby (~> 1.0, >= 1.0.2) 59 | i18n (>= 1.6, < 2) 60 | minitest (>= 5.1) 61 | tzinfo (~> 2.0) 62 | zeitwerk (~> 2.3) 63 | addressable (2.7.0) 64 | public_suffix (>= 2.0.2, < 5.0) 65 | bindex (0.8.1) 66 | builder (3.2.4) 67 | byebug (11.1.3) 68 | capybara (3.35.1) 69 | addressable 70 | mini_mime (>= 0.1.3) 71 | nokogiri (~> 1.8) 72 | rack (>= 1.6.0) 73 | rack-test (>= 0.6.3) 74 | regexp_parser (>= 1.5, < 3.0) 75 | xpath (~> 3.2) 76 | childprocess (3.0.0) 77 | concurrent-ruby (1.1.8) 78 | connection_pool (2.2.3) 79 | crass (1.0.6) 80 | erubi (1.10.0) 81 | ffi (1.14.2) 82 | globalid (0.4.2) 83 | activesupport (>= 4.2.0) 84 | i18n (1.8.7) 85 | concurrent-ruby (~> 1.0) 86 | jbuilder (2.11.2) 87 | activesupport (>= 5.0.0) 88 | listen (3.4.1) 89 | rb-fsevent (~> 0.10, >= 0.10.3) 90 | rb-inotify (~> 0.9, >= 0.9.10) 91 | loofah (2.9.0) 92 | crass (~> 1.0.2) 93 | nokogiri (>= 1.5.9) 94 | mail (2.7.1) 95 | mini_mime (>= 0.1.1) 96 | marcel (0.3.3) 97 | mimemagic (~> 0.3.2) 98 | method_source (1.0.0) 99 | mimemagic (0.3.5) 100 | mini_mime (1.0.2) 101 | mini_portile2 (2.5.0) 102 | minitest (5.14.3) 103 | nio4r (2.5.4) 104 | nokogiri (1.11.1) 105 | mini_portile2 (~> 2.5.0) 106 | racc (~> 1.4) 107 | nokogiri (1.11.1-x86_64-darwin) 108 | racc (~> 1.4) 109 | public_suffix (4.0.6) 110 | puma (5.2.0) 111 | nio4r (~> 2.0) 112 | racc (1.5.2) 113 | rack (2.2.3) 114 | rack-mini-profiler (2.3.0) 115 | rack (>= 1.2.0) 116 | rack-proxy (0.6.5) 117 | rack 118 | rack-test (1.1.0) 119 | rack (>= 1.0, < 3) 120 | rails (6.1.1) 121 | actioncable (= 6.1.1) 122 | actionmailbox (= 6.1.1) 123 | actionmailer (= 6.1.1) 124 | actionpack (= 6.1.1) 125 | actiontext (= 6.1.1) 126 | actionview (= 6.1.1) 127 | activejob (= 6.1.1) 128 | activemodel (= 6.1.1) 129 | activerecord (= 6.1.1) 130 | activestorage (= 6.1.1) 131 | activesupport (= 6.1.1) 132 | bundler (>= 1.15.0) 133 | railties (= 6.1.1) 134 | sprockets-rails (>= 2.0.0) 135 | rails-dom-testing (2.0.3) 136 | activesupport (>= 4.2.0) 137 | nokogiri (>= 1.6) 138 | rails-html-sanitizer (1.3.0) 139 | loofah (~> 2.3) 140 | railties (6.1.1) 141 | actionpack (= 6.1.1) 142 | activesupport (= 6.1.1) 143 | method_source 144 | rake (>= 0.8.7) 145 | thor (~> 1.0) 146 | rake (13.0.3) 147 | rb-fsevent (0.10.4) 148 | rb-inotify (0.10.1) 149 | ffi (~> 1.0) 150 | redis (4.2.5) 151 | regexp_parser (2.0.3) 152 | rubyzip (2.3.0) 153 | sass-rails (6.0.0) 154 | sassc-rails (~> 2.1, >= 2.1.1) 155 | sassc (2.4.0) 156 | ffi (~> 1.9) 157 | sassc-rails (2.1.2) 158 | railties (>= 4.0.0) 159 | sassc (>= 2.0) 160 | sprockets (> 3.0) 161 | sprockets-rails 162 | tilt 163 | selenium-webdriver (3.142.7) 164 | childprocess (>= 0.5, < 4.0) 165 | rubyzip (>= 1.2.2) 166 | semantic_range (2.3.1) 167 | sidekiq (6.1.2) 168 | connection_pool (>= 2.2.2) 169 | rack (~> 2.0) 170 | redis (>= 4.2.0) 171 | spring (2.1.1) 172 | sprockets (4.0.2) 173 | concurrent-ruby (~> 1.0) 174 | rack (> 1, < 3) 175 | sprockets-rails (3.2.2) 176 | actionpack (>= 4.0) 177 | activesupport (>= 4.0) 178 | sprockets (>= 3.0.0) 179 | sqlite3 (1.4.2) 180 | thor (1.1.0) 181 | tilt (2.0.10) 182 | turbolinks (5.2.1) 183 | turbolinks-source (~> 5.2) 184 | turbolinks-source (5.2.0) 185 | tzinfo (2.0.4) 186 | concurrent-ruby (~> 1.0) 187 | web-console (4.1.0) 188 | actionview (>= 6.0.0) 189 | activemodel (>= 6.0.0) 190 | bindex (>= 0.4.0) 191 | railties (>= 6.0.0) 192 | webdrivers (4.5.0) 193 | nokogiri (~> 1.6) 194 | rubyzip (>= 1.3.0) 195 | selenium-webdriver (>= 3.0, < 4.0) 196 | webpacker (5.2.1) 197 | activesupport (>= 5.2) 198 | rack-proxy (>= 0.6.1) 199 | railties (>= 5.2) 200 | semantic_range (>= 2.3.0) 201 | websocket-driver (0.7.3) 202 | websocket-extensions (>= 0.1.0) 203 | websocket-extensions (0.1.5) 204 | xpath (3.2.0) 205 | nokogiri (~> 1.8) 206 | zeitwerk (2.4.2) 207 | 208 | PLATFORMS 209 | aarch64-linux 210 | x86_64-darwin-20 211 | 212 | DEPENDENCIES 213 | byebug 214 | capybara (>= 3.26) 215 | jbuilder (~> 2.7) 216 | listen (~> 3.3) 217 | puma (~> 5.0) 218 | rack-mini-profiler (~> 2.0) 219 | rails (~> 6.1.0) 220 | sass-rails (>= 6) 221 | selenium-webdriver 222 | sidekiq 223 | spring 224 | sqlite3 (~> 1.4) 225 | turbolinks (~> 5) 226 | tzinfo-data 227 | web-console (>= 4.1.0) 228 | webdrivers 229 | webpacker (~> 5.0) 230 | 231 | RUBY VERSION 232 | ruby 2.7.2p137 233 | 234 | BUNDLED WITH 235 | 2.2.4 236 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Automate Rails Provision and Deployment using Ansible

2 | 3 | You can find a more in-depth [**Blog Post Here**](https://www.pedroalonso.net/blog/automate-rails-provision-and-deployment-using-ansible) 4 | 5 | This is a sample Rails 6.1 app with 2 Ansible Playbooks, inside `.ansible-deploy` folder. Provision playbook is used to easily provision an **Ubuntu 20.04 Server**, there's another playbook for deployment and rollback your Rails app using Ansistrano. 6 | 7 | While this is meant to work out of the box, you can tweak the files in the `roles` directory in order to satisfy your project-specific requirements. 8 | 9 | --- 10 | 11 | ### What does this do? 12 | * Configure Ubuntu 20.04 Server with some sensible defaults. 13 | * Install required/useful packages. You can [**update this list here**](https://github.com/pedropaf/rails-ansible-provision-deployment/blob/bb402ad2777dc6cf9d4e7225fb85cd0eafb2a3a1/.ansible-deploy/app-vars.yml#L53-L75). 14 | * Auto upgrade all installed packages 15 | * Create a new deployment user (called 'deploy') with passwordless login 16 | * SSH hardening 17 | * Prevent password login 18 | * Change the default SSH port 19 | * Prevent root login 20 | * Setup UFW (firewall) 21 | * Setup Fail2ban 22 | * Install Logrotate 23 | * Setup Nginx with some sensible config (thanks to nginxconfig.io) 24 | * Certbot, for Let's encrypt SSL certificates. 25 | * Ruby (using Rbenv). 26 | * Defaults to `2.7.2`. You can change it in the [`app-vars.yml`](https://github.com/pedropaf/rails-ansible-provision-deployment/blob/bb402ad2777dc6cf9d4e7225fb85cd0eafb2a3a1/.ansible-deploy/app-vars.yml#L78) file 27 | * [jemmaloc](https://github.com/jemalloc/jemalloc) is also installed and configured by default 28 | * [rbenv-vars](https://github.com/rbenv/rbenv-vars) is also installed by default 29 | * Node.js 30 | * Defaults to 15.x. You can change it in the [`app-vars.yml`](https://github.com/pedropaf/rails-ansible-provision-deployment/blob/bb402ad2777dc6cf9d4e7225fb85cd0eafb2a3a1/.ansible-deploy/app-vars.yml#L88) file. 31 | * Yarn 32 | * Redis (latest) 33 | * Postgresql. 34 | * Defaults to v13. You can specify the version that you need in the `app-vars.yml` file. 35 | * Puma (with Systemd support for restarting automatically) **See Puma Config section below**. 36 | * Sidekiq (with Systemd support for restarting automatically) 37 | * Ansistrano hooks for performing the following tasks - 38 | * Installing all our gems 39 | * Precompiling assets 40 | * Migrating our database (using `run_once`) 41 | 42 | --- 43 | 44 | ## Getting started 45 | Here are the steps that you need to follow in order to get up and running with Ansible Rails. 46 | 47 | ### Step 1. Installation 48 | 49 | You can just copy the `.ansible-deploy` folder in your Rails application folder, or clone this repo. 50 | 51 | ### Step 2. Storing sensitive data for Ansible 52 | As mentioned earlier, we have one Ansible Playbook to setup the serve, so the secret variables that are needed to setup the server, are stored in an Ansible Vault. The secrets related to the Rails app, should be stored using [Custom Credentials](https://edgeguides.rubyonrails.org/security.html#custom-credentials). 53 | 54 | To create a new `Ansible Vault`(https://docs.ansible.com/ansible/latest/user_guide/vault.html) file to store sensitive information: 55 | 56 | ```bash 57 | $ ansible-vault create .ansible-deploy/group_vars/all/vault.yml 58 | ``` 59 | 60 | Add the following information to this new vault file 61 | ```yaml 62 | vault_postgresql_db_password: "XXXXX_SUPER_SECURE_PASS_XXXXX" 63 | vault_rails_master_key: "XXXXX_MASTER_KEY_FOR_RAILS_XXXXX" 64 | ``` 65 | 66 | ### Step 3. Configuration 67 | Configure the relevant variables in `app-vars.yml`. 68 | 69 | ```yaml 70 | app_name: YOUR_APP_NAME # Replace with name of your app 71 | app_git_repo: "YOUR_GIT_REPO" 72 | app_git_branch: "main" # branch that you want to deploy (e.g: 'production') 73 | 74 | postgresql_db_user: "{{ deploy_user }}_postgresql_user" 75 | postgresql_db_password: "{{ vault_postgresql_db_password }}" # from vault (see previous section) 76 | postgresql_db_name: "{{ app_name }}_production" 77 | ``` 78 | 79 | ### Step 4. Server Provisioning 80 | 81 | If you have booted up a clean Ubuntu Server, you can install all the dependencies for your Rails application running: 82 | 83 | ```bash 84 | $ cd .ansible-deploy 85 | $ ansible-playbook -i inventories/development.ini provision.yml 86 | ``` 87 | 88 | In Ansible, an inventory is called the list of server(s) where you want to run your playbook. If you want to use a different staging / production servers, you can have 2 inventory files one for each stage, called `staging.ini` / `production.ini`. 89 | To deploy this app to your production server, you can use [DigitalOcean](https://www.digitalocean.com), AWS or any other hosting provider of your preference. 90 | 91 | ```yaml 92 | [web] 93 | 192.168.0.1 # replace with IP address of your server. 94 | 95 | [all:vars] 96 | ansible_ssh_user=deployer 97 | ansible_python_interpreter=/usr/bin/python3 98 | ansible_ssh_private_key_file="~/.ssh/id_rsa" 99 | ``` 100 | 101 | ### Step 5. Deploy your application 102 | 103 | After your server is setup (step 4), you can deploy your application running: 104 | 105 | ```bash 106 | $ cd .ansible-deploy 107 | $ ansible-playbook -i inventories/development.ini deploy.yml 108 | ``` 109 | 110 | ## Additional Configuration 111 | ### Installing additional packages 112 | You can add/remove packages to this list by changing the `required_package` variable in `app-vars.yml`. 113 | ### Uncomplicated Firewall Configuration (UFW) 114 | 115 | Uncomplicated Firewall is enabled and accepting connections from any IP on ports 22 (ssh), 80 (http), and 443 (https). Feel free to update it in `app-vars.yml`. 116 | ### Configure Certbot (Let's Encrypt SSL certificates) 117 | 118 | Certboot is configured to request, install and set up a CRON job to update your certificate when it expires. Setup your domain DNS as well as the details for your domain / email to request the certificate in [`app-vars.yml`](https://github.com/pedropaf/rails-ansible-provision-deployment/blob/bb402ad2777dc6cf9d4e7225fb85cd0eafb2a3a1/.ansible-deploy/app-vars.yml#L103). 119 | ### PostgreSQL Database Backups 120 | By default, daily backup is enabled in the `app-vars.yml` file. In order for this to work, the following variables need to be set. If you do not wish to store backups, remove (or uncomment) these lines from `app-vars.yml`. 121 | 122 | ```yaml 123 | aws_key: "{{ vault_aws_key }}" # store this in group_vars/all/vault.yml that we created earlier 124 | aws_secret: "{{ vault_aws_secret }}" 125 | 126 | postgresql_backup_dir: "{{ deploy_user_path }}/backups" 127 | postgresql_backup_filename_format: >- 128 | {{ app_name }}-%Y%m%d-%H%M%S.pgdump 129 | postgresql_db_backup_healthcheck: "NOTIFICATION_URL (eg: https://healthcheck.io/)" # optional 130 | postgresql_s3_backup_bucket: "DB_BACKUP_BUCKET" # name of the S3 bucket to store backups 131 | postgresql_s3_backup_hour: "3" 132 | postgresql_s3_backup_minute: "*" 133 | postgresql_s3_backup_delete_after: "7 days" # days after which old backups should be deleted 134 | ``` 135 | 136 | #### Puma config 137 | 138 | Update your [**config/puma.rb**](https://github.com/pedropaf/rails-ansible-provision-deployment/blob/main/config/puma.rb) with settings for production. 139 | 140 | ```ruby 141 | max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } 142 | min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } 143 | threads min_threads_count, max_threads_count 144 | 145 | port ENV.fetch("PORT") { 3000 } 146 | 147 | rails_env = ENV.fetch("RAILS_ENV") { "development" } 148 | environment rails_env 149 | 150 | if %w[production staging].member?(rails_env) 151 | app_dir = ENV.fetch("APP_DIR") { "YOUR_APP/current" } 152 | directory app_dir 153 | 154 | shared_dir = ENV.fetch("SHARED_DIR") { "YOUR_APP/shared" } 155 | 156 | # Logging 157 | stdout_redirect "#{shared_dir}/log/puma.stdout.log", "#{shared_dir}/log/puma.stderr.log", true 158 | 159 | pidfile "#{shared_dir}/tmp/pids/puma.pid" 160 | state_path "#{shared_dir}/tmp/pids/puma.state" 161 | 162 | # Set up socket location 163 | bind "unix://#{shared_dir}/sockets/puma.sock" 164 | 165 | workers ENV.fetch("WEB_CONCURRENCY") { 2 } 166 | preload_app! 167 | 168 | elsif rails_env == "development" 169 | # Specifies the `worker_timeout` threshold that Puma will use to wait before 170 | # terminating a worker in development environments. 171 | worker_timeout 3600 172 | plugin :tmp_restart 173 | end 174 | ``` 175 | 176 | --- 177 | 178 | ### Credits 179 | 180 | These 2 repos were a useful inspiration and starting point: 181 | 182 | * [dresden-weekly/ansible-rails](https://github.com/dresden-weekly/ansible-rails) 183 | * [emailthis/ansible-rails](https://github.com/EmailThis/ansible-rails) 184 | --- 185 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative "config/application" 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../stylesheets .css 3 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedropaf/rails-ansible-provision-deployment/68b57fd549b58ea596a0badfaf929712d5e39d36/app/assets/images/.keep -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's 6 | * vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | -------------------------------------------------------------------------------- /app/assets/stylesheets/index.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the index controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: https://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | end 3 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedropaf/rails-ansible-provision-deployment/68b57fd549b58ea596a0badfaf929712d5e39d36/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/index_controller.rb: -------------------------------------------------------------------------------- 1 | class IndexController < ApplicationController 2 | def index 3 | HardWorker.perform_async('bob', 5) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/index_helper.rb: -------------------------------------------------------------------------------- 1 | module IndexHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/javascript/channels/consumer.js: -------------------------------------------------------------------------------- 1 | // Action Cable provides the framework to deal with WebSockets in Rails. 2 | // You can generate new channels where WebSocket features live using the `bin/rails generate channel` command. 3 | 4 | import { createConsumer } from "@rails/actioncable" 5 | 6 | export default createConsumer() 7 | -------------------------------------------------------------------------------- /app/javascript/channels/index.js: -------------------------------------------------------------------------------- 1 | // Load all the channels within this directory and all subdirectories. 2 | // Channel files must be named *_channel.js. 3 | 4 | const channels = require.context('.', true, /_channel\.js$/) 5 | channels.keys().forEach(channels) 6 | -------------------------------------------------------------------------------- /app/javascript/packs/application.js: -------------------------------------------------------------------------------- 1 | // This file is automatically compiled by Webpack, along with any other files 2 | // present in this directory. You're encouraged to place your actual application logic in 3 | // a relevant structure within app/javascript and only use these pack files to reference 4 | // that code so it'll be compiled. 5 | 6 | import Rails from "@rails/ujs" 7 | import Turbolinks from "turbolinks" 8 | import * as ActiveStorage from "@rails/activestorage" 9 | import "channels" 10 | 11 | Rails.start() 12 | Turbolinks.start() 13 | ActiveStorage.start() 14 | -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | # Automatically retry jobs that encountered a deadlock 3 | # retry_on ActiveRecord::Deadlocked 4 | 5 | # Most jobs are safe to ignore if the underlying records are no longer available 6 | # discard_on ActiveJob::DeserializationError 7 | end 8 | -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: 'from@example.com' 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedropaf/rails-ansible-provision-deployment/68b57fd549b58ea596a0badfaf929712d5e39d36/app/models/concerns/.keep -------------------------------------------------------------------------------- /app/views/index/index.html.erb: -------------------------------------------------------------------------------- 1 |

Hello Rails!

-------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RailsAnsibleDeploy 5 | 6 | <%= csrf_meta_tags %> 7 | <%= csp_meta_tag %> 8 | 9 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> 10 | <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %> 11 | 12 | 13 | 14 | <%= yield %> 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /app/workers/hard_worker.rb: -------------------------------------------------------------------------------- 1 | class HardWorker 2 | include Sidekiq::Worker 3 | 4 | def perform(*args) 5 | # Do something 6 | Rails.logger.info "Things are happening in worker." 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | var validEnv = ['development', 'test', 'production'] 3 | var currentEnv = api.env() 4 | var isDevelopmentEnv = api.env('development') 5 | var isProductionEnv = api.env('production') 6 | var isTestEnv = api.env('test') 7 | 8 | if (!validEnv.includes(currentEnv)) { 9 | throw new Error( 10 | 'Please specify a valid `NODE_ENV` or ' + 11 | '`BABEL_ENV` environment variables. Valid values are "development", ' + 12 | '"test", and "production". Instead, received: ' + 13 | JSON.stringify(currentEnv) + 14 | '.' 15 | ) 16 | } 17 | 18 | return { 19 | presets: [ 20 | isTestEnv && [ 21 | '@babel/preset-env', 22 | { 23 | targets: { 24 | node: 'current' 25 | } 26 | } 27 | ], 28 | (isProductionEnv || isDevelopmentEnv) && [ 29 | '@babel/preset-env', 30 | { 31 | forceAllTransforms: true, 32 | useBuiltIns: 'entry', 33 | corejs: 3, 34 | modules: false, 35 | exclude: ['transform-typeof-symbol'] 36 | } 37 | ] 38 | ].filter(Boolean), 39 | plugins: [ 40 | 'babel-plugin-macros', 41 | '@babel/plugin-syntax-dynamic-import', 42 | isTestEnv && 'babel-plugin-dynamic-import-node', 43 | '@babel/plugin-transform-destructuring', 44 | [ 45 | '@babel/plugin-proposal-class-properties', 46 | { 47 | loose: true 48 | } 49 | ], 50 | [ 51 | '@babel/plugin-proposal-object-rest-spread', 52 | { 53 | useBuiltIns: true 54 | } 55 | ], 56 | [ 57 | '@babel/plugin-transform-runtime', 58 | { 59 | helpers: false 60 | } 61 | ], 62 | [ 63 | '@babel/plugin-transform-regenerator', 64 | { 65 | async: false 66 | } 67 | ] 68 | ].filter(Boolean) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'bundle' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | require "rubygems" 12 | 13 | m = Module.new do 14 | module_function 15 | 16 | def invoked_as_script? 17 | File.expand_path($0) == File.expand_path(__FILE__) 18 | end 19 | 20 | def env_var_version 21 | ENV["BUNDLER_VERSION"] 22 | end 23 | 24 | def cli_arg_version 25 | return unless invoked_as_script? # don't want to hijack other binstubs 26 | return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update` 27 | bundler_version = nil 28 | update_index = nil 29 | ARGV.each_with_index do |a, i| 30 | if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN 31 | bundler_version = a 32 | end 33 | next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/ 34 | bundler_version = $1 35 | update_index = i 36 | end 37 | bundler_version 38 | end 39 | 40 | def gemfile 41 | gemfile = ENV["BUNDLE_GEMFILE"] 42 | return gemfile if gemfile && !gemfile.empty? 43 | 44 | File.expand_path("../../Gemfile", __FILE__) 45 | end 46 | 47 | def lockfile 48 | lockfile = 49 | case File.basename(gemfile) 50 | when "gems.rb" then gemfile.sub(/\.rb$/, gemfile) 51 | else "#{gemfile}.lock" 52 | end 53 | File.expand_path(lockfile) 54 | end 55 | 56 | def lockfile_version 57 | return unless File.file?(lockfile) 58 | lockfile_contents = File.read(lockfile) 59 | return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/ 60 | Regexp.last_match(1) 61 | end 62 | 63 | def bundler_version 64 | @bundler_version ||= 65 | env_var_version || cli_arg_version || 66 | lockfile_version 67 | end 68 | 69 | def bundler_requirement 70 | return "#{Gem::Requirement.default}.a" unless bundler_version 71 | 72 | bundler_gem_version = Gem::Version.new(bundler_version) 73 | 74 | requirement = bundler_gem_version.approximate_recommendation 75 | 76 | return requirement unless Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.7.0") 77 | 78 | requirement += ".a" if bundler_gem_version.prerelease? 79 | 80 | requirement 81 | end 82 | 83 | def load_bundler! 84 | ENV["BUNDLE_GEMFILE"] ||= gemfile 85 | 86 | activate_bundler 87 | end 88 | 89 | def activate_bundler 90 | gem_error = activation_error_handling do 91 | gem "bundler", bundler_requirement 92 | end 93 | return if gem_error.nil? 94 | require_error = activation_error_handling do 95 | require "bundler/version" 96 | end 97 | return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION)) 98 | warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`" 99 | exit 42 100 | end 101 | 102 | def activation_error_handling 103 | yield 104 | nil 105 | rescue StandardError, LoadError => e 106 | e 107 | end 108 | end 109 | 110 | m.load_bundler! 111 | 112 | if m.invoked_as_script? 113 | load Gem.bin_path("bundler", "bundle") 114 | end 115 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | load File.expand_path("spring", __dir__) 3 | APP_PATH = File.expand_path('../config/application', __dir__) 4 | require_relative "../config/boot" 5 | require "rails/commands" 6 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | load File.expand_path("spring", __dir__) 3 | require_relative "../config/boot" 4 | require "rake" 5 | Rake.application.run 6 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "fileutils" 3 | 4 | # path to your application root. 5 | APP_ROOT = File.expand_path('..', __dir__) 6 | 7 | def system!(*args) 8 | system(*args) || abort("\n== Command #{args} failed ==") 9 | end 10 | 11 | FileUtils.chdir APP_ROOT do 12 | # This script is a way to set up or update your development environment automatically. 13 | # This script is idempotent, so that you can run it at any time and get an expectable outcome. 14 | # Add necessary setup steps to this file. 15 | 16 | puts '== Installing dependencies ==' 17 | system! 'gem install bundler --conservative' 18 | system('bundle check') || system!('bundle install') 19 | 20 | # Install JavaScript dependencies 21 | system! 'bin/yarn' 22 | 23 | # puts "\n== Copying sample files ==" 24 | # unless File.exist?('config/database.yml') 25 | # FileUtils.cp 'config/database.yml.sample', 'config/database.yml' 26 | # end 27 | 28 | puts "\n== Preparing database ==" 29 | system! 'bin/rails db:prepare' 30 | 31 | puts "\n== Removing old logs and tempfiles ==" 32 | system! 'bin/rails log:clear tmp:clear' 33 | 34 | puts "\n== Restarting application server ==" 35 | system! 'bin/rails restart' 36 | end 37 | -------------------------------------------------------------------------------- /bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | if !defined?(Spring) && [nil, "development", "test"].include?(ENV["RAILS_ENV"]) 3 | # Load Spring without loading other gems in the Gemfile, for speed. 4 | require "bundler" 5 | Bundler.locked_gems.specs.find { |spec| spec.name == "spring" }&.tap do |spring| 6 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path 7 | gem "spring", spring.version 8 | require "spring/binstub" 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /bin/webpack: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" 4 | ENV["NODE_ENV"] ||= "development" 5 | 6 | require "pathname" 7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 8 | Pathname.new(__FILE__).realpath) 9 | 10 | require "bundler/setup" 11 | 12 | require "webpacker" 13 | require "webpacker/webpack_runner" 14 | 15 | APP_ROOT = File.expand_path("..", __dir__) 16 | Dir.chdir(APP_ROOT) do 17 | Webpacker::WebpackRunner.run(ARGV) 18 | end 19 | -------------------------------------------------------------------------------- /bin/webpack-dev-server: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" 4 | ENV["NODE_ENV"] ||= "development" 5 | 6 | require "pathname" 7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 8 | Pathname.new(__FILE__).realpath) 9 | 10 | require "bundler/setup" 11 | 12 | require "webpacker" 13 | require "webpacker/dev_server_runner" 14 | 15 | APP_ROOT = File.expand_path("..", __dir__) 16 | Dir.chdir(APP_ROOT) do 17 | Webpacker::DevServerRunner.run(ARGV) 18 | end 19 | -------------------------------------------------------------------------------- /bin/yarn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | 4 | APP_ROOT = File.expand_path('..', __dir__) 5 | Dir.chdir(APP_ROOT) do 6 | executable_path = ENV["PATH"].split(File::PATH_SEPARATOR).find do |path| 7 | normalized_path = File.expand_path(path) 8 | 9 | normalized_path != __dir__ && File.executable?(Pathname.new(normalized_path).join('yarn')) 10 | end 11 | 12 | if executable_path 13 | exec File.expand_path(Pathname.new(executable_path).join('yarn')), *ARGV 14 | else 15 | $stderr.puts "Yarn executable was not detected in the system." 16 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" 17 | exit 1 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative "config/environment" 4 | 5 | run Rails.application 6 | Rails.application.load_server 7 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative "boot" 2 | 3 | require "rails/all" 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | module RailsAnsibleDeploy 10 | class Application < Rails::Application 11 | # Initialize configuration defaults for originally generated Rails version. 12 | config.load_defaults 6.1 13 | 14 | # Configuration for the application, engines, and railties goes here. 15 | # 16 | # These settings can be overridden in specific environments using the files 17 | # in config/environments, which are processed later. 18 | # 19 | # config.time_zone = "Central Time (US & Canada)" 20 | # config.eager_load_paths << Rails.root.join("extras") 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 2 | 3 | require "bundler/setup" # Set up gems listed in the Gemfile. 4 | # require "bootsnap/setup" # Speed up boot time by caching expensive operations. 5 | -------------------------------------------------------------------------------- /config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: test 6 | 7 | production: 8 | adapter: redis 9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> 10 | channel_prefix: rails_ansible_deploy_production 11 | -------------------------------------------------------------------------------- /config/credentials.yml.enc: -------------------------------------------------------------------------------- 1 | rxelnRXV8jIOH39V8PoD7QU25b5GDf3JupyDZkv4p1FKljeQCrSOfwWxbc2IRywL1I31hWceoaQYtLXp0HLRRWnTrzJxg1d4GrJeKD/zIQG7POaZpGdz8mW8z4NFGuFMDpq+1MaCDLcQLJ0SzqlbkGwGH4pvqBHWgwvUlz2+2Ko8cbfYr8AxGCz5RY/9opspeoyXaTsDSWh2zPuYX5G1EMJb4kMFiQ52cmDV4CLI5cvvAr7S5rf5/Yf2zE8pKxSG0lG0IUDd27zJXDkNLv8fmsaz9cWipZFt1WolRGj/zPfu8TeslmFo+tcb8yCACwiHQKThf4n3LovgqbPzWTzzkLKVUitT3aENxjxOvjk5TC1QmWOPfzoyf+kIJpJRMj7P952NJ0+CWJev/VaMpLpPrFyPDg8FrAbLE9ec--jzRjCldfKsTfhiuv--8wKqrAyyj9S8bCFLQW6brA== -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite. Versions 3.8.0 and up are supported. 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | # 7 | default: &default 8 | adapter: sqlite3 9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 10 | timeout: 5000 11 | 12 | development: 13 | <<: *default 14 | database: db/development.sqlite3 15 | 16 | # Warning: The database defined as "test" will be erased and 17 | # re-generated from your development database when you run "rake". 18 | # Do not set this db to the same as development or production. 19 | test: 20 | <<: *default 21 | database: db/test.sqlite3 22 | 23 | production: 24 | <<: *default 25 | database: db/production.sqlite3 26 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative "application" 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | 7 | Rails.logger = Logger.new(STDOUT) 8 | Rails.logger = ActiveSupport::Logger.new("log/#{Rails.env}.log") 9 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | require "active_support/core_ext/integer/time" 2 | 3 | Rails.application.configure do 4 | # Settings specified here will take precedence over those in config/application.rb. 5 | 6 | # In the development environment your application's code is reloaded any time 7 | # it changes. This slows down response time but is perfect for development 8 | # since you don't have to restart the web server when you make code changes. 9 | config.cache_classes = false 10 | 11 | # Do not eager load code on boot. 12 | config.eager_load = false 13 | 14 | # Show full error reports. 15 | config.consider_all_requests_local = true 16 | 17 | # Enable/disable caching. By default caching is disabled. 18 | # Run rails dev:cache to toggle caching. 19 | if Rails.root.join('tmp', 'caching-dev.txt').exist? 20 | config.action_controller.perform_caching = true 21 | config.action_controller.enable_fragment_cache_logging = true 22 | 23 | config.cache_store = :memory_store 24 | config.public_file_server.headers = { 25 | 'Cache-Control' => "public, max-age=#{2.days.to_i}" 26 | } 27 | else 28 | config.action_controller.perform_caching = false 29 | 30 | config.cache_store = :null_store 31 | end 32 | 33 | # Store uploaded files on the local file system (see config/storage.yml for options). 34 | config.active_storage.service = :local 35 | 36 | # Don't care if the mailer can't send. 37 | config.action_mailer.raise_delivery_errors = false 38 | 39 | config.action_mailer.perform_caching = false 40 | 41 | # Print deprecation notices to the Rails logger. 42 | config.active_support.deprecation = :log 43 | 44 | # Raise exceptions for disallowed deprecations. 45 | config.active_support.disallowed_deprecation = :raise 46 | 47 | # Tell Active Support which deprecation messages to disallow. 48 | config.active_support.disallowed_deprecation_warnings = [] 49 | 50 | # Raise an error on page load if there are pending migrations. 51 | config.active_record.migration_error = :page_load 52 | 53 | # Highlight code that triggered database queries in logs. 54 | config.active_record.verbose_query_logs = true 55 | 56 | # Debug mode disables concatenation and preprocessing of assets. 57 | # This option may cause significant delays in view rendering with a large 58 | # number of complex assets. 59 | config.assets.debug = true 60 | 61 | # Suppress logger output for asset requests. 62 | config.assets.quiet = true 63 | 64 | # Raises error for missing translations. 65 | # config.i18n.raise_on_missing_translations = true 66 | 67 | # Annotate rendered view with file names. 68 | # config.action_view.annotate_rendered_view_with_filenames = true 69 | 70 | # Use an evented file watcher to asynchronously detect changes in source code, 71 | # routes, locales, etc. This feature depends on the listen gem. 72 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker 73 | 74 | # Uncomment if you wish to allow Action Cable access from any origin. 75 | # config.action_cable.disable_request_forgery_protection = true 76 | end 77 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | require "active_support/core_ext/integer/time" 2 | 3 | Rails.application.configure do 4 | # Settings specified here will take precedence over those in config/application.rb. 5 | 6 | # Code is not reloaded between requests. 7 | config.cache_classes = true 8 | 9 | # Eager load code on boot. This eager loads most of Rails and 10 | # your application in memory, allowing both threaded web servers 11 | # and those relying on copy on write to perform better. 12 | # Rake tasks automatically ignore this option for performance. 13 | config.eager_load = true 14 | 15 | # Full error reports are disabled and caching is turned on. 16 | config.consider_all_requests_local = false 17 | config.action_controller.perform_caching = true 18 | 19 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] 20 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). 21 | # config.require_master_key = true 22 | 23 | # Disable serving static files from the `/public` folder by default since 24 | # Apache or NGINX already handles this. 25 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? 26 | 27 | # Compress CSS using a preprocessor. 28 | # config.assets.css_compressor = :sass 29 | 30 | # Do not fallback to assets pipeline if a precompiled asset is missed. 31 | config.assets.compile = false 32 | 33 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 34 | # config.asset_host = 'http://assets.example.com' 35 | 36 | # Specifies the header that your server uses for sending files. 37 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 38 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 39 | 40 | # Store uploaded files on the local file system (see config/storage.yml for options). 41 | config.active_storage.service = :local 42 | 43 | # Mount Action Cable outside main process or domain. 44 | # config.action_cable.mount_path = nil 45 | # config.action_cable.url = 'wss://example.com/cable' 46 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] 47 | 48 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 49 | # config.force_ssl = true 50 | 51 | # Include generic and useful information about system operation, but avoid logging too much 52 | # information to avoid inadvertent exposure of personally identifiable information (PII). 53 | config.log_level = :info 54 | 55 | # Prepend all log lines with the following tags. 56 | config.log_tags = [ :request_id ] 57 | 58 | # Use a different cache store in production. 59 | # config.cache_store = :mem_cache_store 60 | 61 | # Use a real queuing backend for Active Job (and separate queues per environment). 62 | # config.active_job.queue_adapter = :resque 63 | # config.active_job.queue_name_prefix = "rails_ansible_deploy_production" 64 | 65 | config.action_mailer.perform_caching = false 66 | 67 | # Ignore bad email addresses and do not raise email delivery errors. 68 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 69 | # config.action_mailer.raise_delivery_errors = false 70 | 71 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 72 | # the I18n.default_locale when a translation cannot be found). 73 | config.i18n.fallbacks = true 74 | 75 | # Send deprecation notices to registered listeners. 76 | config.active_support.deprecation = :notify 77 | 78 | # Log disallowed deprecations. 79 | config.active_support.disallowed_deprecation = :log 80 | 81 | # Tell Active Support which deprecation messages to disallow. 82 | config.active_support.disallowed_deprecation_warnings = [] 83 | 84 | # Use default logging formatter so that PID and timestamp are not suppressed. 85 | config.log_formatter = ::Logger::Formatter.new 86 | 87 | # Use a different logger for distributed setups. 88 | # require "syslog/logger" 89 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 90 | 91 | if ENV["RAILS_LOG_TO_STDOUT"].present? 92 | logger = ActiveSupport::Logger.new(STDOUT) 93 | logger.formatter = config.log_formatter 94 | config.logger = ActiveSupport::TaggedLogging.new(logger) 95 | end 96 | 97 | # Do not dump schema after migrations. 98 | config.active_record.dump_schema_after_migration = false 99 | 100 | # Inserts middleware to perform automatic connection switching. 101 | # The `database_selector` hash is used to pass options to the DatabaseSelector 102 | # middleware. The `delay` is used to determine how long to wait after a write 103 | # to send a subsequent read to the primary. 104 | # 105 | # The `database_resolver` class is used by the middleware to determine which 106 | # database is appropriate to use based on the time delay. 107 | # 108 | # The `database_resolver_context` class is used by the middleware to set 109 | # timestamps for the last write to the primary. The resolver uses the context 110 | # class timestamps to determine how long to wait before reading from the 111 | # replica. 112 | # 113 | # By default Rails will store a last write timestamp in the session. The 114 | # DatabaseSelector middleware is designed as such you can define your own 115 | # strategy for connection switching and pass that into the middleware through 116 | # these configuration options. 117 | # config.active_record.database_selector = { delay: 2.seconds } 118 | # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver 119 | # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session 120 | end 121 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | require "active_support/core_ext/integer/time" 2 | 3 | # The test environment is used exclusively to run your application's 4 | # test suite. You never need to work with it otherwise. Remember that 5 | # your test database is "scratch space" for the test suite and is wiped 6 | # and recreated between test runs. Don't rely on the data there! 7 | 8 | Rails.application.configure do 9 | # Settings specified here will take precedence over those in config/application.rb. 10 | 11 | config.cache_classes = false 12 | config.action_view.cache_template_loading = true 13 | 14 | # Do not eager load code on boot. This avoids loading your whole application 15 | # just for the purpose of running a single test. If you are using a tool that 16 | # preloads Rails for running tests, you may have to set it to true. 17 | config.eager_load = false 18 | 19 | # Configure public file server for tests with Cache-Control for performance. 20 | config.public_file_server.enabled = true 21 | config.public_file_server.headers = { 22 | 'Cache-Control' => "public, max-age=#{1.hour.to_i}" 23 | } 24 | 25 | # Show full error reports and disable caching. 26 | config.consider_all_requests_local = true 27 | config.action_controller.perform_caching = false 28 | config.cache_store = :null_store 29 | 30 | # Raise exceptions instead of rendering exception templates. 31 | config.action_dispatch.show_exceptions = false 32 | 33 | # Disable request forgery protection in test environment. 34 | config.action_controller.allow_forgery_protection = false 35 | 36 | # Store uploaded files on the local file system in a temporary directory. 37 | config.active_storage.service = :test 38 | 39 | config.action_mailer.perform_caching = false 40 | 41 | # Tell Action Mailer not to deliver emails to the real world. 42 | # The :test delivery method accumulates sent emails in the 43 | # ActionMailer::Base.deliveries array. 44 | config.action_mailer.delivery_method = :test 45 | 46 | # Print deprecation notices to the stderr. 47 | config.active_support.deprecation = :stderr 48 | 49 | # Raise exceptions for disallowed deprecations. 50 | config.active_support.disallowed_deprecation = :raise 51 | 52 | # Tell Active Support which deprecation messages to disallow. 53 | config.active_support.disallowed_deprecation_warnings = [] 54 | 55 | # Raises error for missing translations. 56 | # config.i18n.raise_on_missing_translations = true 57 | 58 | # Annotate rendered view with file names. 59 | # config.action_view.annotate_rendered_view_with_filenames = true 60 | end 61 | -------------------------------------------------------------------------------- /config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ActiveSupport::Reloader.to_prepare do 4 | # ApplicationController.renderer.defaults.merge!( 5 | # http_host: 'example.org', 6 | # https: false 7 | # ) 8 | # end 9 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path. 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | # Add Yarn node_modules folder to the asset load path. 9 | Rails.application.config.assets.paths << Rails.root.join('node_modules') 10 | 11 | # Precompile additional assets. 12 | # application.js, application.css, and all non-JS/CSS in the app/assets 13 | # folder are already added. 14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) 15 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| /my_noisy_library/.match?(line) } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code 7 | # by setting BACKTRACE=1 before calling your invocation, like "BACKTRACE=1 ./bin/rails runner 'MyClass.perform'". 8 | Rails.backtrace_cleaner.remove_silencers! if ENV["BACKTRACE"] 9 | -------------------------------------------------------------------------------- /config/initializers/content_security_policy.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Define an application-wide content security policy 4 | # For further information see the following documentation 5 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy 6 | 7 | # Rails.application.config.content_security_policy do |policy| 8 | # policy.default_src :self, :https 9 | # policy.font_src :self, :https, :data 10 | # policy.img_src :self, :https, :data 11 | # policy.object_src :none 12 | # policy.script_src :self, :https 13 | # policy.style_src :self, :https 14 | # # If you are using webpack-dev-server then specify webpack-dev-server host 15 | # policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development? 16 | 17 | # # Specify URI for violation reports 18 | # # policy.report_uri "/csp-violation-report-endpoint" 19 | # end 20 | 21 | # If you are using UJS then enable automatic nonce generation 22 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) } 23 | 24 | # Set the nonce only to specific directives 25 | # Rails.application.config.content_security_policy_nonce_directives = %w(script-src) 26 | 27 | # Report CSP violations to a specified URI 28 | # For further information see the following documentation: 29 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only 30 | # Rails.application.config.content_security_policy_report_only = true 31 | -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [ 5 | :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn 6 | ] 7 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /config/initializers/permissions_policy.rb: -------------------------------------------------------------------------------- 1 | # Define an application-wide HTTP permissions policy. For further 2 | # information see https://developers.google.com/web/updates/2018/06/feature-policy 3 | # 4 | # Rails.application.config.permissions_policy do |f| 5 | # f.camera :none 6 | # f.gyroscope :none 7 | # f.microphone :none 8 | # f.usb :none 9 | # f.fullscreen :self 10 | # f.payment :self, "https://secure.example.com" 11 | # end 12 | -------------------------------------------------------------------------------- /config/initializers/sidekiq.rb: -------------------------------------------------------------------------------- 1 | Rails.logger = Sidekiq.logger 2 | ActiveRecord::Base.logger = Sidekiq.logger -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # The following keys must be escaped otherwise they will not be retrieved by 20 | # the default I18n backend: 21 | # 22 | # true, false, on, off, yes, no 23 | # 24 | # Instead, surround them with single quotes. 25 | # 26 | # en: 27 | # 'true': 'foo' 28 | # 29 | # To learn more, please read the Rails Internationalization guide 30 | # available at https://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | hello: "Hello world" 34 | -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | # Puma can serve each request in a thread from an internal thread pool. 2 | # The `threads` method setting takes two numbers: a minimum and maximum. 3 | # Any libraries that use thread pools should be configured to match 4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 5 | # and maximum; this matches the default thread size of Active Record. 6 | # 7 | max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } 8 | min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } 9 | threads min_threads_count, max_threads_count 10 | 11 | port ENV.fetch("PORT") { 3000 } 12 | 13 | rails_env = ENV.fetch("RAILS_ENV") { "development" } 14 | environment rails_env 15 | 16 | if %w[production staging].member?(rails_env) 17 | app_dir = ENV.fetch("APP_DIR") { "YOUR_APP/current" } 18 | directory app_dir 19 | 20 | shared_dir = ENV.fetch("SHARED_DIR") { "YOUR_APP/shared" } 21 | 22 | # Logging 23 | stdout_redirect "#{shared_dir}/log/puma.stdout.log", "#{shared_dir}/log/puma.stderr.log", true 24 | 25 | pidfile "#{shared_dir}/tmp/pids/puma.pid" 26 | state_path "#{shared_dir}/tmp/pids/puma.state" 27 | 28 | # Set up socket location 29 | bind "unix://#{shared_dir}/sockets/puma.sock" 30 | 31 | workers ENV.fetch("WEB_CONCURRENCY") { 2 } 32 | preload_app! 33 | 34 | elsif rails_env == "development" 35 | # Specifies the `worker_timeout` threshold that Puma will use to wait before 36 | # terminating a worker in development environments. 37 | worker_timeout 3600 38 | plugin :tmp_restart 39 | end -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root 'index#index' 3 | 4 | require 'sidekiq/web' 5 | mount Sidekiq::Web => '/sidekiq' 6 | end 7 | -------------------------------------------------------------------------------- /config/sidekiq.yml: -------------------------------------------------------------------------------- 1 | --- 2 | :concurrency: 5 3 | staging: 4 | :concurrency: 10 5 | production: 6 | :concurrency: 10 7 | :queues: 8 | - critical 9 | - default 10 | - low -------------------------------------------------------------------------------- /config/spring.rb: -------------------------------------------------------------------------------- 1 | Spring.watch( 2 | ".ruby-version", 3 | ".rbenv-vars", 4 | "tmp/restart.txt", 5 | "tmp/caching-dev.txt" 6 | ) 7 | -------------------------------------------------------------------------------- /config/storage.yml: -------------------------------------------------------------------------------- 1 | test: 2 | service: Disk 3 | root: <%= Rails.root.join("tmp/storage") %> 4 | 5 | local: 6 | service: Disk 7 | root: <%= Rails.root.join("storage") %> 8 | 9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) 10 | # amazon: 11 | # service: S3 12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> 13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> 14 | # region: us-east-1 15 | # bucket: your_own_bucket 16 | 17 | # Remember not to checkin your GCS keyfile to a repository 18 | # google: 19 | # service: GCS 20 | # project: your_project 21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> 22 | # bucket: your_own_bucket 23 | 24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) 25 | # microsoft: 26 | # service: AzureStorage 27 | # storage_account_name: your_account_name 28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> 29 | # container: your_container_name 30 | 31 | # mirror: 32 | # service: Mirror 33 | # primary: local 34 | # mirrors: [ amazon, google, microsoft ] 35 | -------------------------------------------------------------------------------- /config/webpack/development.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development' 2 | 3 | const environment = require('./environment') 4 | 5 | module.exports = environment.toWebpackConfig() 6 | -------------------------------------------------------------------------------- /config/webpack/environment.js: -------------------------------------------------------------------------------- 1 | const { environment } = require('@rails/webpacker') 2 | 3 | module.exports = environment 4 | -------------------------------------------------------------------------------- /config/webpack/production.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = process.env.NODE_ENV || 'production' 2 | 3 | const environment = require('./environment') 4 | 5 | module.exports = environment.toWebpackConfig() 6 | -------------------------------------------------------------------------------- /config/webpack/test.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development' 2 | 3 | const environment = require('./environment') 4 | 5 | module.exports = environment.toWebpackConfig() 6 | -------------------------------------------------------------------------------- /config/webpacker.yml: -------------------------------------------------------------------------------- 1 | # Note: You must restart bin/webpack-dev-server for changes to take effect 2 | 3 | default: &default 4 | source_path: app/javascript 5 | source_entry_path: packs 6 | public_root_path: public 7 | public_output_path: packs 8 | cache_path: tmp/cache/webpacker 9 | webpack_compile_output: true 10 | 11 | # Additional paths webpack should lookup modules 12 | # ['app/assets', 'engine/foo/app/assets'] 13 | additional_paths: [] 14 | 15 | # Reload manifest.json on all requests so we reload latest compiled packs 16 | cache_manifest: false 17 | 18 | # Extract and emit a css file 19 | extract_css: false 20 | 21 | static_assets_extensions: 22 | - .jpg 23 | - .jpeg 24 | - .png 25 | - .gif 26 | - .tiff 27 | - .ico 28 | - .svg 29 | - .eot 30 | - .otf 31 | - .ttf 32 | - .woff 33 | - .woff2 34 | 35 | extensions: 36 | - .mjs 37 | - .js 38 | - .sass 39 | - .scss 40 | - .css 41 | - .module.sass 42 | - .module.scss 43 | - .module.css 44 | - .png 45 | - .svg 46 | - .gif 47 | - .jpeg 48 | - .jpg 49 | 50 | development: 51 | <<: *default 52 | compile: true 53 | 54 | # Reference: https://webpack.js.org/configuration/dev-server/ 55 | dev_server: 56 | https: false 57 | host: localhost 58 | port: 3035 59 | public: localhost:3035 60 | hmr: false 61 | # Inline should be set to true if using HMR 62 | inline: true 63 | overlay: true 64 | compress: true 65 | disable_host_check: true 66 | use_local_ip: false 67 | quiet: false 68 | pretty: false 69 | headers: 70 | 'Access-Control-Allow-Origin': '*' 71 | watch_options: 72 | ignored: '**/node_modules/**' 73 | 74 | 75 | test: 76 | <<: *default 77 | compile: true 78 | 79 | # Compile test packs to a separate directory 80 | public_output_path: packs-test 81 | 82 | production: 83 | <<: *default 84 | 85 | # Production depends on precompilation of packs prior to booting for performance. 86 | compile: false 87 | 88 | # Extract and emit a css file 89 | extract_css: true 90 | 91 | # Cache manifest.json for performance 92 | cache_manifest: true 93 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) 7 | # Character.create(name: 'Luke', movie: movies.first) 8 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedropaf/rails-ansible-provision-deployment/68b57fd549b58ea596a0badfaf929712d5e39d36/lib/assets/.keep -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedropaf/rails-ansible-provision-deployment/68b57fd549b58ea596a0badfaf929712d5e39d36/lib/tasks/.keep -------------------------------------------------------------------------------- /log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedropaf/rails-ansible-provision-deployment/68b57fd549b58ea596a0badfaf929712d5e39d36/log/.keep -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rails-ansible-deploy", 3 | "private": true, 4 | "dependencies": { 5 | "@rails/actioncable": "^6.0.0", 6 | "@rails/activestorage": "^6.0.0", 7 | "@rails/ujs": "^6.0.0", 8 | "@rails/webpacker": "5.2.1", 9 | "turbolinks": "^5.2.0" 10 | }, 11 | "version": "0.1.0", 12 | "devDependencies": { 13 | "webpack-dev-server": "^3.11.2" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('postcss-import'), 4 | require('postcss-flexbugs-fixes'), 5 | require('postcss-preset-env')({ 6 | autoprefixer: { 7 | flexbox: 'no-2009' 8 | }, 9 | stage: 3 10 | }) 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The page you were looking for doesn't exist.

62 |

You may have mistyped the address or the page may have moved.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The change you wanted was rejected.

62 |

Maybe you tried to change something you didn't have access to.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

We're sorry, but something went wrong.

62 |
63 |

If you are the application owner check the logs for more information.

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedropaf/rails-ansible-provision-deployment/68b57fd549b58ea596a0badfaf929712d5e39d36/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedropaf/rails-ansible-provision-deployment/68b57fd549b58ea596a0badfaf929712d5e39d36/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedropaf/rails-ansible-provision-deployment/68b57fd549b58ea596a0badfaf929712d5e39d36/public/favicon.ico -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | -------------------------------------------------------------------------------- /storage/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedropaf/rails-ansible-provision-deployment/68b57fd549b58ea596a0badfaf929712d5e39d36/storage/.keep -------------------------------------------------------------------------------- /test/application_system_test_case.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ApplicationSystemTestCase < ActionDispatch::SystemTestCase 4 | driven_by :selenium, using: :chrome, screen_size: [1400, 1400] 5 | end 6 | -------------------------------------------------------------------------------- /test/channels/application_cable/connection_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ApplicationCable::ConnectionTest < ActionCable::Connection::TestCase 4 | # test "connects with cookies" do 5 | # cookies.signed[:user_id] = 42 6 | # 7 | # connect 8 | # 9 | # assert_equal connection.user_id, "42" 10 | # end 11 | end 12 | -------------------------------------------------------------------------------- /test/controllers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedropaf/rails-ansible-provision-deployment/68b57fd549b58ea596a0badfaf929712d5e39d36/test/controllers/.keep -------------------------------------------------------------------------------- /test/controllers/index_controller_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class IndexControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/fixtures/files/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedropaf/rails-ansible-provision-deployment/68b57fd549b58ea596a0badfaf929712d5e39d36/test/fixtures/files/.keep -------------------------------------------------------------------------------- /test/helpers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedropaf/rails-ansible-provision-deployment/68b57fd549b58ea596a0badfaf929712d5e39d36/test/helpers/.keep -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedropaf/rails-ansible-provision-deployment/68b57fd549b58ea596a0badfaf929712d5e39d36/test/integration/.keep -------------------------------------------------------------------------------- /test/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedropaf/rails-ansible-provision-deployment/68b57fd549b58ea596a0badfaf929712d5e39d36/test/mailers/.keep -------------------------------------------------------------------------------- /test/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedropaf/rails-ansible-provision-deployment/68b57fd549b58ea596a0badfaf929712d5e39d36/test/models/.keep -------------------------------------------------------------------------------- /test/system/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedropaf/rails-ansible-provision-deployment/68b57fd549b58ea596a0badfaf929712d5e39d36/test/system/.keep -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | require_relative "../config/environment" 3 | require "rails/test_help" 4 | 5 | class ActiveSupport::TestCase 6 | # Run tests in parallel with specified workers 7 | parallelize(workers: :number_of_processors) 8 | 9 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. 10 | fixtures :all 11 | 12 | # Add more helper methods to be used by all tests here... 13 | end 14 | -------------------------------------------------------------------------------- /test/workers/hard_worker_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | class HardWorkerTest < Minitest::Test 3 | def test_example 4 | skip "add some examples to (or delete) #{__FILE__}" 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /tmp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedropaf/rails-ansible-provision-deployment/68b57fd549b58ea596a0badfaf929712d5e39d36/tmp/.keep -------------------------------------------------------------------------------- /tmp/pids/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedropaf/rails-ansible-provision-deployment/68b57fd549b58ea596a0badfaf929712d5e39d36/tmp/pids/.keep -------------------------------------------------------------------------------- /vendor/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedropaf/rails-ansible-provision-deployment/68b57fd549b58ea596a0badfaf929712d5e39d36/vendor/.keep --------------------------------------------------------------------------------