diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4d50199 --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +# See https://help.github.com/articles/ignoring-files for more about ignoring files. +# +# If you find yourself ignoring temporary files generated by your text editor +# or operating system, you probably want to add a global ignore instead: +# git config --global core.excludesfile '~/.gitignore_global' + +# Ignore bundler config. +/.bundle + +# Ignore all logfiles and tempfiles. +/log/* +/tmp/* +!/log/.keep +!/tmp/.keep + +# Ignore Byebug command history file. +.byebug_history + +# Ignore test reports +/brakeman-output.* +/coverage + +# Ignore results of `rake assets:precompile`. +/public/assets/ + +# Never check passwords into source control! +/config/database.yml +/.env diff --git a/.overcommit.yml b/.overcommit.yml new file mode 100644 index 0000000..6ceea30 --- /dev/null +++ b/.overcommit.yml @@ -0,0 +1,44 @@ +# Use this file to configure the Overcommit hooks you wish to use. This will +# extend the default configuration defined in: +# https://github.com/brigade/overcommit/blob/master/config/default.yml +# +# At the topmost level of this YAML file is a key representing type of hook +# being run (e.g. pre-commit, commit-msg, etc.). Within each type you can +# customize each hook, such as whether to only run it on certain files (via +# `include`), whether to only display output if it fails (via `quiet`), etc. +# +# For a complete list of hooks, see: +# https://github.com/brigade/overcommit/tree/master/lib/overcommit/hook +# +# For a complete list of options that you can use to customize hooks, see: +# https://github.com/brigade/overcommit#configuration +# +# Uncomment the following lines to make the configuration take effect. + +gemfile: Gemfile + +PreCommit: + BundleCheck: + enabled: true + + LocalPathsInGemfile: + enabled: true + + RailsSchemaUpToDate: + enabled: true + + RuboCop: + enabled: true + on_warn: fail + + TrailingWhitespace: + enabled: true + exclude: + - "**/db/structure.sql" + + YamlSyntax: + enabled: true + +PostCheckout: + ALL: + quiet: true diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..276cbf9 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +2.3.0 diff --git a/.simplecov b/.simplecov new file mode 100644 index 0000000..499c5d0 --- /dev/null +++ b/.simplecov @@ -0,0 +1,8 @@ +require "simplecov" +SimpleCov.start("rails") do + add_filter("/bin/") + add_filter("/lib/tasks/auto_annotate_models.rake") + add_filter("/lib/tasks/coverage.rake") +end +SimpleCov.minimum_coverage(90) +SimpleCov.use_merging(false) diff --git a/Capfile b/Capfile new file mode 100644 index 0000000..95a23f6 --- /dev/null +++ b/Capfile @@ -0,0 +1,15 @@ +# Load DSL and set up stages +require "capistrano/setup" + +# Include default deployment tasks +require "capistrano/deploy" + +# Include tasks from other gems included in your Gemfile +require "airbrussh/capistrano" +require "capistrano/bundler" +require "capistrano/rails" +require "capistrano/mb" +require "capistrano-nc/nc" + +# Load custom tasks from `lib/capistrano/tasks` if you have any defined +Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r } diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..143a4aa --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,84 @@ +# How to deploy mena-devs_com + +This document covers the steps need to deploy the application to an existing environment. To create a new environment, refer to `PROVISIONING.md`. + +* **Developers:** this project is built and deployed using Capistrano, which is the `cap` command. Refer to the *Capistrano* section. +* **System administrators:** once deployed, the various processes and configuration can be maintained with familiar Linux commands. Refer to the *Server maintenance* section. + + +## Capistrano + +### Environments + +* **staging** is deployed from the `development` branch. +* **production** is deployed from the `master` branch. + +### Prerequisites + +Capistrano runs on your local machine and uses SSH to perform the deployment on the remote server. Therefore: + +* The Capistrano gem must be installed (see `README.md` for project setup instructions). +* You must have SSH access to the production/staging server. +* Your SSH key must be installed on the server in `~deployer/.ssh/authorized_keys`. +* You must have SSH access to Git repository (using your SSH key). + +### Performing a graceful deployment (no migrations) + +This will deploy the latest code from `development`. Make sure to `git push` your changes first, or they will not apply. + +``` +bundle exec cap production deploy +``` + +### Performing a full deployment + +If there are data migrations or other changes that require downtime, perform the deployment using the following task: + +``` +bundle exec cap production deploy:migrate_and_restart +``` + +This will stop the app and display a maintenance page during the deployment. + + +## Server maintenance + +This application consists of two executables that run as the `deployer` user: + +* Unicorn is the application server that services Rails HTTP requests +* Sidekiq is the background worker that services the job queue stored in Redis + +These in turn rely on the following services: + +* PostgreSQL +* Redis +* Nginx + +### Controlling processes + +All processes are set up to start automatically when the server boots, and can be controlled using the standard Ubuntu `service` command: + +``` +sudo service postgresql +sudo service nginx +sudo service unicorn_mena_devs_com +sudo service sidekiq_mena_devs_com +``` + +Note that Unicorn and Sidekiq require access to PostgreSQL and Redis, so those supporting services should be started first. + +### Configuration + +All configuration values required by the application can be changed here: + +``` +/home/deployer/apps/mena_devs_com/shared/.env.production +``` + +After making changes to this file, be sure to restart the application: + +``` +sudo service unicorn_mena_devs_com restart +sudo service sidekiq_mena_devs_com stop +sudo service sidekiq_mena_devs_com start +``` diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..bd31945 --- /dev/null +++ b/Gemfile @@ -0,0 +1,71 @@ +source "https://rubygems.org" + +gem "active_type", ">= 0.3.2" +gem "autoprefixer-rails", ">= 5.0.0.1" +gem "bcrypt", "~> 3.1.7" +gem "bootstrap_form", "~> 2.3" +gem "bootstrap-sass", "~> 3.3" +gem "bootscale", :require => false +gem "coffee-rails", "~> 4.2" +gem "dotenv-rails", ">= 2.0.0" +gem "font-awesome-rails" +gem "jquery-rails" +gem "mail", ">= 2.6.3" +gem "marco-polo" +gem "pg", "~> 0.18" +gem "pgcli-rails" +gem "rails", "5.0.0.1" +gem "redis-namespace" +gem "sass-rails", "~> 5.0" +gem "secure_headers", "~> 3.0" +gem "sidekiq", ">= 4.2.0" +gem "turbolinks", "~> 5" + +group :production, :staging do + gem "postmark-rails" + gem "unicorn" + gem "unicorn-worker-killer" +end + +group :development do + gem "annotate", ">= 2.5.0" + gem "awesome_print" + gem "better_errors" + gem "binding_of_caller" + gem "brakeman", :require => false + gem "bundler-audit", ">= 0.5.0", :require => false + gem "capistrano", "~> 3.6", :require => false + gem "capistrano-bundler", "~> 1.2", :require => false + gem "capistrano-mb", ">= 0.22.2", :require => false + gem "capistrano-nc", :require => false + gem "capistrano-rails", :require => false + gem "guard", ">= 2.2.2", :require => false + gem "guard-livereload", :require => false + gem "guard-minitest", :require => false + gem "letter_opener" + gem "listen", "~> 3.0.5" + gem "overcommit", :require => false + gem "puma", "~> 3.0" + gem "rack-livereload" + gem "rb-fsevent", :require => false + gem "rubocop", :require => false + gem "simplecov", :require => false + gem "spring" + gem "sshkit", "~> 1.8", :require => false + gem "spring-watcher-listen", "~> 2.0.0" + gem "terminal-notifier", :require => false + gem "terminal-notifier-guard", :require => false + gem "xray-rails", ">= 0.1.18" +end + +group :test do + gem "capybara" + gem "connection_pool" + gem "launchy" + gem "minitest-capybara" + gem "minitest-reporters" + gem "mocha" + gem "poltergeist" + gem "shoulda-context" + gem "shoulda-matchers", ">= 3.0.1" +end diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..db501b7 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,402 @@ +GEM + remote: https://rubygems.org/ + specs: + actioncable (5.0.0.1) + actionpack (= 5.0.0.1) + nio4r (~> 1.2) + websocket-driver (~> 0.6.1) + actionmailer (5.0.0.1) + actionpack (= 5.0.0.1) + actionview (= 5.0.0.1) + activejob (= 5.0.0.1) + mail (~> 2.5, >= 2.5.4) + rails-dom-testing (~> 2.0) + actionpack (5.0.0.1) + actionview (= 5.0.0.1) + activesupport (= 5.0.0.1) + rack (~> 2.0) + rack-test (~> 0.6.3) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + actionview (5.0.0.1) + activesupport (= 5.0.0.1) + builder (~> 3.1) + erubis (~> 2.7.0) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + active_type (0.6.0) + activerecord (>= 3.2) + activejob (5.0.0.1) + activesupport (= 5.0.0.1) + globalid (>= 0.3.6) + activemodel (5.0.0.1) + activesupport (= 5.0.0.1) + activerecord (5.0.0.1) + activemodel (= 5.0.0.1) + activesupport (= 5.0.0.1) + arel (~> 7.0) + activesupport (5.0.0.1) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (~> 0.7) + minitest (~> 5.1) + tzinfo (~> 1.1) + addressable (2.5.0) + public_suffix (~> 2.0, >= 2.0.2) + airbrussh (1.1.1) + sshkit (>= 1.6.1, != 1.7.0) + annotate (2.7.1) + activerecord (>= 3.2, < 6.0) + rake (>= 10.4, < 12.0) + ansi (1.5.0) + arel (7.1.4) + ast (2.3.0) + autoprefixer-rails (6.5.3) + execjs + awesome_print (1.7.0) + bcrypt (3.1.11) + better_errors (2.1.1) + coderay (>= 1.0.0) + erubis (>= 2.6.6) + rack (>= 0.9.0) + binding_of_caller (0.7.2) + debug_inspector (>= 0.0.1) + bootscale (0.5.2) + bootstrap-sass (3.3.7) + autoprefixer-rails (>= 5.2.1) + sass (>= 3.3.4) + bootstrap_form (2.5.2) + brakeman (3.4.1) + builder (3.2.2) + bundler-audit (0.5.0) + bundler (~> 1.2) + thor (~> 0.18) + capistrano (3.6.1) + airbrussh (>= 1.0.0) + capistrano-harrow + i18n + rake (>= 10.0.0) + sshkit (>= 1.9.0) + capistrano-bundler (1.2.0) + capistrano (~> 3.1) + sshkit (~> 1.2) + capistrano-harrow (0.5.3) + capistrano-mb (0.31.0) + capistrano (>= 3.3.5) + sshkit (>= 1.6.1) + capistrano-nc (0.1.4) + capistrano (~> 3.0) + terminal-notifier (~> 1.6) + capistrano-rails (1.2.0) + capistrano (~> 3.1) + capistrano-bundler (~> 1.1) + capybara (2.10.1) + addressable + mime-types (>= 1.16) + nokogiri (>= 1.3.3) + rack (>= 1.0.0) + rack-test (>= 0.5.4) + xpath (~> 2.0) + childprocess (0.5.9) + ffi (~> 1.0, >= 1.0.11) + cliver (0.3.2) + coderay (1.1.1) + coffee-rails (4.2.1) + coffee-script (>= 2.2.0) + railties (>= 4.0.0, < 5.2.x) + coffee-script (2.4.1) + coffee-script-source + execjs + coffee-script-source (1.11.1) + concurrent-ruby (1.0.2) + connection_pool (2.2.1) + debug_inspector (0.0.2) + docile (1.1.5) + dotenv (2.1.1) + dotenv-rails (2.1.1) + dotenv (= 2.1.1) + railties (>= 4.0, < 5.1) + em-websocket (0.5.1) + eventmachine (>= 0.12.9) + http_parser.rb (~> 0.6.0) + erubis (2.7.0) + eventmachine (1.2.1) + execjs (2.7.0) + ffi (1.9.14) + font-awesome-rails (4.7.0.0) + railties (>= 3.2, < 5.1) + formatador (0.2.5) + get_process_mem (0.2.1) + globalid (0.3.7) + activesupport (>= 4.1.0) + guard (2.14.0) + formatador (>= 0.2.4) + listen (>= 2.7, < 4.0) + lumberjack (~> 1.0) + nenv (~> 0.1) + notiffany (~> 0.0) + pry (>= 0.9.12) + shellany (~> 0.0) + thor (>= 0.18.1) + guard-compat (1.2.1) + guard-livereload (2.5.2) + em-websocket (~> 0.5) + guard (~> 2.8) + guard-compat (~> 1.0) + multi_json (~> 1.8) + guard-minitest (2.4.6) + guard-compat (~> 1.2) + minitest (>= 3.0) + http_parser.rb (0.6.0) + i18n (0.7.0) + iniparse (1.4.2) + jquery-rails (4.2.1) + rails-dom-testing (>= 1, < 3) + railties (>= 4.2.0) + thor (>= 0.14, < 2.0) + json (2.0.2) + kgio (2.10.0) + launchy (2.4.3) + addressable (~> 2.3) + letter_opener (1.4.1) + launchy (~> 2.2) + listen (3.0.8) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + loofah (2.0.3) + nokogiri (>= 1.5.9) + lumberjack (1.0.10) + mail (2.6.4) + mime-types (>= 1.16, < 4) + marco-polo (1.2.1) + metaclass (0.0.4) + method_source (0.8.2) + mime-types (3.1) + mime-types-data (~> 3.2015) + mime-types-data (3.2016.0521) + mini_portile2 (2.1.0) + minitest (5.9.1) + minitest-capybara (0.8.2) + capybara (~> 2.2) + minitest (~> 5.0) + rake + minitest-reporters (1.1.12) + ansi + builder + minitest (>= 5.0) + ruby-progressbar + mocha (1.2.1) + metaclass (~> 0.0.1) + multi_json (1.12.1) + nenv (0.3.0) + net-scp (1.2.1) + net-ssh (>= 2.6.5) + net-ssh (3.2.0) + nio4r (1.2.1) + nokogiri (1.6.8.1) + mini_portile2 (~> 2.1.0) + notiffany (0.1.1) + nenv (~> 0.1) + shellany (~> 0.0) + overcommit (0.37.0) + childprocess (~> 0.5.8) + iniparse (~> 1.4) + parser (2.3.3.0) + ast (~> 2.2) + pg (0.19.0) + pgcli-rails (0.2.1) + railties (>= 4.2.0) + poltergeist (1.11.0) + capybara (~> 2.1) + cliver (~> 0.3.1) + websocket-driver (>= 0.2.0) + postmark (1.10.0) + json + rake + postmark-rails (0.15.0) + actionmailer (>= 3.0.0) + postmark (~> 1.10.0) + powerpack (0.1.1) + pry (0.10.4) + coderay (~> 1.1.0) + method_source (~> 0.8.1) + slop (~> 3.4) + public_suffix (2.0.4) + puma (3.6.2) + rack (2.0.1) + rack-livereload (0.3.16) + rack + rack-protection (1.5.3) + rack + rack-test (0.6.3) + rack (>= 1.0) + rails (5.0.0.1) + actioncable (= 5.0.0.1) + actionmailer (= 5.0.0.1) + actionpack (= 5.0.0.1) + actionview (= 5.0.0.1) + activejob (= 5.0.0.1) + activemodel (= 5.0.0.1) + activerecord (= 5.0.0.1) + activesupport (= 5.0.0.1) + bundler (>= 1.3.0, < 2.0) + railties (= 5.0.0.1) + sprockets-rails (>= 2.0.0) + rails-dom-testing (2.0.1) + activesupport (>= 4.2.0, < 6.0) + nokogiri (~> 1.6.0) + rails-html-sanitizer (1.0.3) + loofah (~> 2.0) + railties (5.0.0.1) + actionpack (= 5.0.0.1) + activesupport (= 5.0.0.1) + method_source + rake (>= 0.8.7) + thor (>= 0.18.1, < 2.0) + rainbow (2.1.0) + raindrops (0.17.0) + rake (11.3.0) + rb-fsevent (0.9.8) + rb-inotify (0.9.7) + ffi (>= 0.5.0) + redis (3.3.2) + redis-namespace (1.5.2) + redis (~> 3.0, >= 3.0.4) + rubocop (0.45.0) + parser (>= 2.3.1.1, < 3.0) + powerpack (~> 0.1) + rainbow (>= 1.99.1, < 3.0) + ruby-progressbar (~> 1.7) + unicode-display_width (~> 1.0, >= 1.0.1) + ruby-progressbar (1.8.1) + sass (3.4.22) + sass-rails (5.0.6) + railties (>= 4.0.0, < 6) + sass (~> 3.1) + sprockets (>= 2.8, < 4.0) + sprockets-rails (>= 2.0, < 4.0) + tilt (>= 1.1, < 3) + secure_headers (3.5.1) + useragent + shellany (0.0.1) + shoulda-context (1.2.2) + shoulda-matchers (3.1.1) + activesupport (>= 4.0.0) + sidekiq (4.2.7) + concurrent-ruby (~> 1.0) + connection_pool (~> 2.2, >= 2.2.0) + rack-protection (>= 1.5.0) + redis (~> 3.2, >= 3.2.1) + simplecov (0.12.0) + docile (~> 1.1.0) + json (>= 1.8, < 3) + simplecov-html (~> 0.10.0) + simplecov-html (0.10.0) + slop (3.6.0) + spring (2.0.0) + activesupport (>= 4.2) + spring-watcher-listen (2.0.1) + listen (>= 2.7, < 4.0) + spring (>= 1.2, < 3.0) + sprockets (3.7.0) + concurrent-ruby (~> 1.0) + rack (> 1, < 3) + sprockets-rails (3.2.0) + actionpack (>= 4.0) + activesupport (>= 4.0) + sprockets (>= 3.0.0) + sshkit (1.11.4) + net-scp (>= 1.1.2) + net-ssh (>= 2.8.0) + terminal-notifier (1.7.1) + terminal-notifier-guard (1.7.0) + thor (0.19.4) + thread_safe (0.3.5) + tilt (2.0.5) + turbolinks (5.0.1) + turbolinks-source (~> 5) + turbolinks-source (5.0.0) + tzinfo (1.2.2) + thread_safe (~> 0.1) + unicode-display_width (1.1.1) + unicorn (5.2.0) + kgio (~> 2.6) + raindrops (~> 0.7) + unicorn-worker-killer (0.4.4) + get_process_mem (~> 0) + unicorn (>= 4, < 6) + useragent (0.16.8) + websocket-driver (0.6.4) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.2) + xpath (2.0.0) + nokogiri (~> 1.3) + xray-rails (0.2.0) + rails (>= 3.1.0) + +PLATFORMS + ruby + +DEPENDENCIES + active_type (>= 0.3.2) + annotate (>= 2.5.0) + autoprefixer-rails (>= 5.0.0.1) + awesome_print + bcrypt (~> 3.1.7) + better_errors + binding_of_caller + bootscale + bootstrap-sass (~> 3.3) + bootstrap_form (~> 2.3) + brakeman + bundler-audit (>= 0.5.0) + capistrano (~> 3.6) + capistrano-bundler (~> 1.2) + capistrano-mb (>= 0.22.2) + capistrano-nc + capistrano-rails + capybara + coffee-rails (~> 4.2) + connection_pool + dotenv-rails (>= 2.0.0) + font-awesome-rails + guard (>= 2.2.2) + guard-livereload + guard-minitest + jquery-rails + launchy + letter_opener + listen (~> 3.0.5) + mail (>= 2.6.3) + marco-polo + minitest-capybara + minitest-reporters + mocha + overcommit + pg (~> 0.18) + pgcli-rails + poltergeist + postmark-rails + puma (~> 3.0) + rack-livereload + rails (= 5.0.0.1) + rb-fsevent + redis-namespace + rubocop + sass-rails (~> 5.0) + secure_headers (~> 3.0) + shoulda-context + shoulda-matchers (>= 3.0.1) + sidekiq (>= 4.2.0) + simplecov + spring + spring-watcher-listen (~> 2.0.0) + sshkit (~> 1.8) + terminal-notifier + terminal-notifier-guard + turbolinks (~> 5) + unicorn + unicorn-worker-killer + xray-rails (>= 0.1.18) + +BUNDLED WITH + 1.13.6 diff --git a/Guardfile b/Guardfile new file mode 100644 index 0000000..28e98b1 --- /dev/null +++ b/Guardfile @@ -0,0 +1,25 @@ +# A sample Guardfile +# More info at https://github.com/guard/guard#readme + +# Run just the livereload monitor with: `guard -P livereload` +guard :livereload do + watch(%r{app/views/.+\.(erb|haml|slim)$}) + watch(%r{app/helpers/.+\.rb}) + watch(%r{public/.+\.(css|js|html)}) + watch(%r{config/locales/.+\.yml}) + # Rails Assets Pipeline + watch(%r{(app|vendor)(/assets/\w+/(.+\.(css|js|html|png|jpg))).*}) { |m| "/assets/#{m[3]}" } + watch(%r{(app|vendor)(/assets/\w+/(.+)\.(scss))}) { |m| "/assets/#{m[3]}.css" } +end + +guard :minitest, :spring => "bin/rails test" do + watch(%r{^app/(.+)\.rb$}) { |m| "test/#{m[1]}_test.rb" } + watch(%r{^app/controllers/application_controller\.rb$}) { "test/controllers" } + watch(%r{^app/controllers/(.+)_controller\.rb$}) { |m| "test/integration/#{m[1]}_test.rb" } + watch(%r{^app/views/(.+)_mailer/.+}) { |m| "test/mailers/#{m[1]}_mailer_test.rb" } + watch(%r{^lib/(.+)\.rb$}) { |m| "test/unit/lib/#{m[1]}_test.rb" } + watch(%r{^lib/tasks/(.+)\.rake$}) { |m| "test/unit/lib/tasks/#{m[1]}_test.rb" } + watch(%r{^test/.+_test\.rb$}) + watch(%r{^test/test_helper\.rb$}) { "test" } + watch(%r{^test/support/.+\.rb}) { "test" } +end diff --git a/PROVISIONING.md b/PROVISIONING.md new file mode 100644 index 0000000..c476e95 --- /dev/null +++ b/PROVISIONING.md @@ -0,0 +1,91 @@ +# How to provision a new environment for mena-devs_com + +This project uses the open source [capistrano-mb](https://github.com/mattbrictson/capistrano-mb) set of tasks for capistrano to provision new environments. This document contains a quick overview of the provisioning process. + +In capistrano terminology, *stage* refers to an environment like `staging` or `production`. These instructions will use the *stage* term for the remainder of the document. + +In this document, *provisioning* refers to preparing a server for deployment, including the following steps: + +* Installing Nginx, Redis, and PostgreSQL +* Compiling Ruby +* Creating the database +* Setting passwords and other configuration + + +## Prerequisites + +Capistrano runs on your local machine and uses SSH to perform the deployment on the remote server. Therefore: + +* The Capistrano gem must be installed (see `README.md` for project setup instructions). +* You must have SSH access to the server. +* Your SSH key must be installed on the server. +* Your account on the server must have `sudo` access. +* Your account must be able to run `sudo` without be prompted for a password. See [How to run sudo command with no password?](http://askubuntu.com/questions/192050/how-to-run-sudo-command-with-no-password). + +Furthermore, the server itself must meet the following requirements: + +* Ubuntu 14.04 LTS (64 bit). + + +## 1. Create a new stage (or edit an existing one) + +Stages are defined as `.rb` files in the `config/deploy/` directory. The name of the file becomes the name of the stage when executing capistrano commands. For example, the production stage is defined in `config/deploy/production.rb`. + +Create a new stage (or modify an existing one to move that stage to a new server address, for example) using the existing stage files as examples. The stage file describes the IP address of the server and other stage-specific information. + +## 2. Run the provision command + +`bundle exec cap provision:14_04` + +Replace `` with the name of the stage you wish to provision (e.g. `production`). + +You will be prompted to choose passwords and enter other configuration values as the script runs. + +It is safe to run the provision command multiple times on the same stage (the scripts are generally idempotent). + +## 3. Install a custom SSL key and certificate using Let's Encrypt with auto-renewal + +Perform the following commands as root. + +``` +mkdir -p /opt/certbot +cd /opt/certbot/ +wget https://dl.eff.org/certbot-auto +chmod a+x certbot-auto +./certbot-auto certonly +``` + +When prompted: + +* Select the *webroot* option +* Enter domains to secure (for example): `mena-devs.com` +* Enter webroot path: `/home/deployer/apps/mena_devs_com/shared/public/` + +The `.pem` files created by `certbot-auto` need to be linked so Nginx can see them: + +``` +ln -s /etc/letsencrypt/live/mena-devs.com/fullchain.pem /etc/ssl/mena_devs_com.crt +ln -s /etc/letsencrypt/live/mena-devs.com/privkey.pem /etc/ssl/mena_devs_com.key +``` + +Restart Nginx to enable the certificate: + +``` +service nginx restart +``` + +Verify auto-renewal works by doing a dry run: + +``` +/opt/certbot/certbot-auto renew --webroot-path /home/deployer/apps/mena_devs_com/shared/public/ --dry-run +``` + +Finally, add this to the root crontab to enable auto-renewal: + +``` +39 9,21 * * * /opt/certbot/certbot-auto renew --quiet --no-self-upgrade --webroot-path /home/deployer/apps/mena_devs_com/shared/public/ +``` + +## 4. Deploy + +Once provisioning is complete, deploy the application using the capistrano instructions in `DEPLOYMENT.md`. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ec308a1 --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +# mena-devs_com + +This is a Rails 5 app. + +## Documentation + +This README describes the purpose of this repository and how to set up a development environment. Other sources of documentation are as follows: + +* UI and API designs are in `doc/` +* Server setup instructions are in `PROVISIONING.md` +* Staging and production deployment instructions are in `DEPLOYMENT.md` + +## Prerequisites + +This project requires: + +* Ruby 2.3.0, preferably managed using [rbenv][] +* PhantomJS (in order to use the [poltergeist][] gem) +* PostgreSQL must be installed and accepting connections +* [Redis][] must be installed and running on localhost with the default port + +On a Mac, you can obtain all of the above packages using [Homebrew][]. + +If you need help setting up a Ruby development environment, check out this [Rails OS X Setup Guide](https://mattbrictson.com/rails-osx-setup-guide). + +## Getting started + +### bin/setup + +Run the `bin/setup` script. This script will: + +* Check you have the required Ruby version +* Install gems using Bundler +* Create local copies of `.env` and `database.yml` +* Create, migrate, and seed the database + +### Run it! + +1. Run `rake test` to make sure everything works. +2. Run `rails s` to start the Rails app. +3. In a separate console, run `bundle exec sidekiq` to start the Sidekiq background job processor. + +[rbenv]:https://github.com/sstephenson/rbenv +[poltergeist]:https://github.com/teampoltergeist/poltergeist +[redis]:http://redis.io +[Homebrew]:http://brew.sh diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..e85f913 --- /dev/null +++ b/Rakefile @@ -0,0 +1,6 @@ +# Add your own tasks in files placed in lib/tasks ending in .rake, +# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. + +require_relative 'config/application' + +Rails.application.load_tasks diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js new file mode 100644 index 0000000..b16e53d --- /dev/null +++ b/app/assets/config/manifest.js @@ -0,0 +1,3 @@ +//= link_tree ../images +//= link_directory ../javascripts .js +//= link_directory ../stylesheets .css diff --git a/app/assets/images/.keep b/app/assets/images/.keep new file mode 100644 index 0000000..e69de29 diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js new file mode 100644 index 0000000..b12018d --- /dev/null +++ b/app/assets/javascripts/application.js @@ -0,0 +1,16 @@ +// This is a manifest file that'll be compiled into application.js, which will include all the files +// listed below. +// +// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, +// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. +// +// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +// compiled file. JavaScript code in this file should be added after the last require_* statement. +// +// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details +// about supported directives. +// +//= require jquery +//= require jquery_ujs +//= require turbolinks +//= require_tree . diff --git a/app/assets/javascripts/bootstrap.js b/app/assets/javascripts/bootstrap.js new file mode 100644 index 0000000..82dd503 --- /dev/null +++ b/app/assets/javascripts/bootstrap.js @@ -0,0 +1,15 @@ +// Protip: remove the parts of Bootstrap that you don't need, in order to +// slim down the amount of JavaScript you send to the browser. +// +//= require bootstrap/affix +//= require bootstrap/alert +//= require bootstrap/button +//= require bootstrap/carousel +//= require bootstrap/collapse +//= require bootstrap/dropdown +//= require bootstrap/tab +//= require bootstrap/transition +//= require bootstrap/scrollspy +//= require bootstrap/modal +//= require bootstrap/tooltip +//= require bootstrap/popover diff --git a/app/assets/javascripts/cable.js b/app/assets/javascripts/cable.js new file mode 100644 index 0000000..71ee1e6 --- /dev/null +++ b/app/assets/javascripts/cable.js @@ -0,0 +1,13 @@ +// Action Cable provides the framework to deal with WebSockets in Rails. +// You can generate new channels where WebSocket features live using the rails generate channel command. +// +//= require action_cable +//= require_self +//= require_tree ./channels + +(function() { + this.App || (this.App = {}); + + App.cable = ActionCable.createConsumer(); + +}).call(this); diff --git a/app/assets/javascripts/channels/.keep b/app/assets/javascripts/channels/.keep new file mode 100644 index 0000000..e69de29 diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss new file mode 100644 index 0000000..2803fe7 --- /dev/null +++ b/app/assets/stylesheets/application.scss @@ -0,0 +1,5 @@ +//= require ./bootstrap +//= require rails_bootstrap_forms +//= require font-awesome +//= require_tree . +//= require_self diff --git a/app/assets/stylesheets/bootstrap.scss b/app/assets/stylesheets/bootstrap.scss new file mode 100644 index 0000000..8b3b9bc --- /dev/null +++ b/app/assets/stylesheets/bootstrap.scss @@ -0,0 +1,64 @@ +// BOOTSTRAP.CSS +// Assembles all the modules of the Twitter Bootstrap framework +// -------------------------------------------------------------------------- + +// Tip: comment out any modules of Bootstrap that you don't need! + +// Sprockets compatibility +@import "bootstrap-sprockets"; + +// ----- Bootstrap modules ----- + +// Core variables and mixins +@import "bootstrap/variables"; +@import "bootstrap/mixins"; + +// Reset and dependencies +@import "bootstrap/normalize"; +@import "bootstrap/print"; +@import "bootstrap/glyphicons"; + +// Core CSS +@import "bootstrap/scaffolding"; +@import "bootstrap/type"; +@import "bootstrap/code"; +@import "bootstrap/grid"; +@import "bootstrap/tables"; +@import "bootstrap/forms"; +@import "bootstrap/buttons"; + +// Components +@import "bootstrap/component-animations"; +@import "bootstrap/dropdowns"; +@import "bootstrap/button-groups"; +@import "bootstrap/input-groups"; +@import "bootstrap/navs"; +@import "bootstrap/navbar"; +@import "bootstrap/breadcrumbs"; +@import "bootstrap/pagination"; +@import "bootstrap/pager"; +@import "bootstrap/labels"; +@import "bootstrap/badges"; +@import "bootstrap/jumbotron"; +@import "bootstrap/thumbnails"; +@import "bootstrap/alerts"; +@import "bootstrap/progress-bars"; +@import "bootstrap/media"; +@import "bootstrap/list-group"; +@import "bootstrap/panels"; +@import "bootstrap/responsive-embed"; +@import "bootstrap/wells"; +@import "bootstrap/close"; + +// Components w/ JavaScript +@import "bootstrap/modals"; +@import "bootstrap/tooltip"; +@import "bootstrap/popovers"; +@import "bootstrap/carousel"; + +// Utility classes +@import "bootstrap/utilities"; +@import "bootstrap/responsive-utilities"; + +// Optional theme +// @import "bootstrap/theme"; diff --git a/app/channels/application_cable/channel.rb b/app/channels/application_cable/channel.rb new file mode 100644 index 0000000..d672697 --- /dev/null +++ b/app/channels/application_cable/channel.rb @@ -0,0 +1,4 @@ +module ApplicationCable + class Channel < ActionCable::Channel::Base + end +end diff --git a/app/channels/application_cable/connection.rb b/app/channels/application_cable/connection.rb new file mode 100644 index 0000000..0ff5442 --- /dev/null +++ b/app/channels/application_cable/connection.rb @@ -0,0 +1,4 @@ +module ApplicationCable + class Connection < ActionCable::Connection::Base + end +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb new file mode 100644 index 0000000..1c07694 --- /dev/null +++ b/app/controllers/application_controller.rb @@ -0,0 +1,3 @@ +class ApplicationController < ActionController::Base + protect_from_forgery with: :exception +end diff --git a/app/controllers/concerns/.keep b/app/controllers/concerns/.keep new file mode 100644 index 0000000..e69de29 diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb new file mode 100644 index 0000000..0507849 --- /dev/null +++ b/app/controllers/home_controller.rb @@ -0,0 +1,5 @@ +# Renders the home page. +class HomeController < ApplicationController + def index + end +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb new file mode 100644 index 0000000..de6be79 --- /dev/null +++ b/app/helpers/application_helper.rb @@ -0,0 +1,2 @@ +module ApplicationHelper +end diff --git a/app/helpers/javascript_helper.rb b/app/helpers/javascript_helper.rb new file mode 100644 index 0000000..026a556 --- /dev/null +++ b/app/helpers/javascript_helper.rb @@ -0,0 +1,16 @@ +module JavascriptHelper + # Adds the async attribute into javascript_include_tag, but only when the + # asset pipeline is not in debug mode. Be careful when using this helper, + # because async ', + javascript_include_async_tag("foo") + ) + end + + # This allows non-existent asset filenames to be used within a test, for + # sprockets-rails >= 3.2.0. + def allow_unknown_assets + return unless respond_to?(:unknown_asset_fallback) + stubs(:unknown_asset_fallback).returns(true) + end +end diff --git a/test/helpers/navbar_helper_test.rb b/test/helpers/navbar_helper_test.rb new file mode 100644 index 0000000..1862dd6 --- /dev/null +++ b/test/helpers/navbar_helper_test.rb @@ -0,0 +1,44 @@ +require "test_helper" + +class NavbarHelperTest < ActionView::TestCase + include NavbarHelper + + test "#navbar_link_to adds active class according to :active_when" do + stubs(:params).returns(:controller => "users") + + tag = navbar_link_to( + "Test", + "http://example.com", + :title => "foo", + :active_when => { :controller => "users" } + ) + + assert_equal( + '
  • '\ + 'Test'\ + "
  • ", + tag + ) + end + + test "#navbar_link_to honors regular expressions" do + stubs(:params).returns(:controller => "users") + + tag = navbar_link_to( + "Test", + "http://example.com", + :active_when => { :controller => /^user.*/ } + ) + + assert_equal( + '
  • Test
  • ', + tag + ) + end + + test "#navbar_link_to otherwise doesn't add active class" do + stubs(:params).returns(:controller => "users") + tag = navbar_link_to("Home", "/", :active_when => { :controller => "home" }) + assert_equal('
  • Home
  • ', tag) + end +end diff --git a/test/helpers/retina_image_helper_test.rb b/test/helpers/retina_image_helper_test.rb new file mode 100644 index 0000000..3266913 --- /dev/null +++ b/test/helpers/retina_image_helper_test.rb @@ -0,0 +1,17 @@ +require "test_helper" + +class RetinaImageHelperTest < ActionView::TestCase + include RetinaImageHelper + + test "retina_image_tag" do + tag = retina_image_tag("example.png", :alt => "example") + + assert_match(%r{\A.*\z}, tag) + assert_match(/alt="example"/, tag) + assert_match(%r{src="/images/example.png"}, tag) + assert_match( + %r{srcset="/images/example.png 1x,/images/example@2x.png 2x"}, + tag + ) + end +end diff --git a/test/integration/.keep b/test/integration/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/integration/layout_helper_test.rb b/test/integration/layout_helper_test.rb new file mode 100644 index 0000000..3531ee5 --- /dev/null +++ b/test/integration/layout_helper_test.rb @@ -0,0 +1,10 @@ +require "test_helper" + +class LayoutHelperTest < ActionDispatch::IntegrationTest + test "rendered page contains both base and application layouts" do + visit("/") + assert_selector("html>head+body") + assert_selector("body p") + assert_match(/Home/, page.title) + end +end diff --git a/test/mailers/.keep b/test/mailers/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/models/.keep b/test/models/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/support/capybara.rb b/test/support/capybara.rb new file mode 100644 index 0000000..7ed05c5 --- /dev/null +++ b/test/support/capybara.rb @@ -0,0 +1,29 @@ +# Capybara + poltergeist allow JS testing via headless webkit +require "capybara/rails" +require "capybara/poltergeist" +Capybara.javascript_driver = :poltergeist + +class ActionDispatch::IntegrationTest + # Make the Capybara DSL available in all integration tests + include Capybara::DSL + # This comes from the minitest-capybara gem + include Capybara::Assertions + + # Ensure each test gets a clean session + setup { Capybara.current_session.driver.browser.clear_cookies } +end + +# Monkey patch so that AR shares a single DB connection among all threads. +# This ensures data consistency between the test thread and poltergeist thread. +# rubocop:disable Style/ClassVars +class ActiveRecord::Base + mattr_accessor :shared_connection + @@shared_connection = nil + + def self.connection + @@shared_connection || begin + ConnectionPool::Wrapper.new(:size => 1) { retrieve_connection } + end + end +end +ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection diff --git a/test/support/mailer.rb b/test/support/mailer.rb new file mode 100644 index 0000000..6263da7 --- /dev/null +++ b/test/support/mailer.rb @@ -0,0 +1,3 @@ +class ActiveSupport::TestCase + setup { ActionMailer::Base.deliveries.clear } +end diff --git a/test/support/mocha.rb b/test/support/mocha.rb new file mode 100644 index 0000000..73d8bfd --- /dev/null +++ b/test/support/mocha.rb @@ -0,0 +1,4 @@ +# Mocha provides mocking and stubbing helpers +require "mocha/mini_test" +Mocha::Configuration.prevent(:stubbing_non_existent_method) +Mocha::Configuration.warn_when(:stubbing_non_public_method) diff --git a/test/support/rails.rb b/test/support/rails.rb new file mode 100644 index 0000000..5de064e --- /dev/null +++ b/test/support/rails.rb @@ -0,0 +1,8 @@ +ENV["RAILS_ENV"] ||= "test" +require File.expand_path("../../../config/environment", __FILE__) +require "rails/test_help" + +class ActiveSupport::TestCase + # Setup all fixtures in test/fixtures/*.yml for all tests in alpha order. + fixtures :all +end diff --git a/test/support/reporters.rb b/test/support/reporters.rb new file mode 100644 index 0000000..8dc80a2 --- /dev/null +++ b/test/support/reporters.rb @@ -0,0 +1,7 @@ +# Minitest::Reporters adds color and progress bar to the test runner +require "minitest/reporters" +Minitest::Reporters.use!( + Minitest::Reporters::ProgressReporter.new, + ENV, + Minitest.backtrace_filter +) diff --git a/test/support/shoulda_matchers.rb b/test/support/shoulda_matchers.rb new file mode 100644 index 0000000..5086d5f --- /dev/null +++ b/test/support/shoulda_matchers.rb @@ -0,0 +1,6 @@ +Shoulda::Matchers.configure do |config| + config.integrate do |with| + with.test_framework :minitest + with.library :rails + end +end diff --git a/test/support/sidekiq.rb b/test/support/sidekiq.rb new file mode 100644 index 0000000..cc974cb --- /dev/null +++ b/test/support/sidekiq.rb @@ -0,0 +1,3 @@ +# Use Sidekiq's test fake that pushes all jobs into a jobs array +require "sidekiq/testing" +Sidekiq::Testing.fake! diff --git a/test/test_helper.rb b/test/test_helper.rb new file mode 100644 index 0000000..c25440e --- /dev/null +++ b/test/test_helper.rb @@ -0,0 +1,5 @@ +# This has to come first +require_relative "./support/rails" + +# Load everything else from test/support +Dir[File.expand_path("../support/**/*.rb", __FILE__)].each { |rb| require(rb) } diff --git a/test/unit/.keep b/test/unit/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/unit/lib/.keep b/test/unit/lib/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/unit/lib/tasks/.keep b/test/unit/lib/tasks/.keep new file mode 100644 index 0000000..e69de29 diff --git a/tmp/.keep b/tmp/.keep new file mode 100644 index 0000000..e69de29 diff --git a/vendor/assets/javascripts/.keep b/vendor/assets/javascripts/.keep new file mode 100644 index 0000000..e69de29 diff --git a/vendor/assets/stylesheets/.keep b/vendor/assets/stylesheets/.keep new file mode 100644 index 0000000..e69de29