├── .coveralls.yml ├── .rspec ├── Gemfile ├── lib ├── dockrails │ ├── version.rb │ └── generate.rb └── dockrails.rb ├── .travis.yml ├── .gitignore ├── spec ├── spec_helper.rb └── generate_spec.rb ├── Rakefile ├── CHANGELOG.md ├── dockrails.gemspec ├── LICENSE ├── Gemfile.lock ├── README.md └── bin └── dockrails /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci 2 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /lib/dockrails/version.rb: -------------------------------------------------------------------------------- 1 | module Dockrails 2 | VERSION = "1.1.0" 3 | end 4 | -------------------------------------------------------------------------------- /lib/dockrails.rb: -------------------------------------------------------------------------------- 1 | require "dockrails/version" 2 | require "dockrails/generate" 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.2 4 | - 2.1 5 | cache: bundler 6 | script: 'bundle exec rake' 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bundle/ 2 | data/ 3 | keys/ 4 | docker-compose.yml 5 | docker-sync.yml 6 | Dockerfile 7 | *.gem 8 | coverage/ 9 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'coveralls' 2 | Coveralls.wear! 3 | 4 | require 'commander' 5 | require 'commander/methods' 6 | require 'pry' 7 | require 'fakefs/spec_helpers' 8 | 9 | # Mock terminal IO streams so we can spec against them 10 | def mock_terminal 11 | @input = StringIO.new 12 | @output = StringIO.new 13 | $terminal = HighLine.new @input, @output 14 | end 15 | 16 | RSpec.configure do |config| 17 | config.expect_with :rspec do |c| 18 | c.syntax = :expect 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | begin 2 | require 'bundler/setup' 3 | rescue LoadError 4 | puts 'You must `gem install bundler` and `bundle install` to run rake tasks' 5 | end 6 | 7 | require 'rdoc/task' 8 | 9 | RDoc::Task.new(:rdoc) do |rdoc| 10 | rdoc.rdoc_dir = 'rdoc' 11 | rdoc.title = 'Dockrails' 12 | rdoc.options << '--line-numbers' 13 | rdoc.rdoc_files.include('README.md') 14 | rdoc.rdoc_files.include('lib/**/*.rb') 15 | end 16 | 17 | Bundler::GemHelper.install_tasks 18 | 19 | require 'rspec/core/rake_task' 20 | require 'bundler/gem_tasks' 21 | 22 | # Default directory to look in is `/specs` 23 | # Run with `rake spec` 24 | RSpec::Core::RakeTask.new(:spec) do |task| 25 | task.rspec_opts = ['--color', '--format', 'documentation'] 26 | end 27 | 28 | task :default => :spec 29 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 4 | 5 | ## [1.1.0] - 2017-12-17 6 | - Now using docker-sync with deamon mode 7 | - Adding new "dockrails logs" command 8 | - Updating "dockrails attach" command to manage CTRL+C exit behavior 9 | 10 | ## [1.0.7] - 2017-12-15 11 | 12 | ### Added 13 | - "restart" command to restart a container 14 | - "stop" alias command to clean 15 | 16 | ## [1.0.6] - 2017-11-02 17 | 18 | ### Added 19 | - "build" command to build or rebuild the docker image 20 | 21 | ## [1.0.4] - 2017-07-26 22 | 23 | ### Added 24 | - A quick "database.yml" info at the end of the init process 25 | 26 | ### Changed 27 | - Updated docker-sync dependency to ~> 0.4.0 (cf [changelog](https://github.com/EugenMayer/docker-sync/wiki/5.-Changelog)) 28 | 29 | ### Fixed 30 | - [Dockrails start fails to start because of docker-sync API change](https://github.com/gmontard/dockrails/issues/2) 31 | -------------------------------------------------------------------------------- /dockrails.gemspec: -------------------------------------------------------------------------------- 1 | $:.push File.expand_path("../lib", __FILE__) 2 | 3 | require "dockrails/version" 4 | 5 | Gem::Specification.new do |s| 6 | s.name = 'dockrails' 7 | s.version = Dockrails::VERSION 8 | s.date = '2017-12-17' 9 | s.summary = "Simple CLI to Generate and Run a Rails environment with Docker!" 10 | s.description = "Docker + Rails + Mac + Dev = <3" 11 | s.homepage = "https://github.com/gmontard/dockrails/" 12 | s.license = "MIT" 13 | s.authors = [ "Guillaume Montard" ] 14 | s.files = Dir["lib/**/*", "LICENSE", "README.me"] 15 | s.test_files = Dir["spec/**/*"] 16 | s.required_ruby_version = ">= 2.0.0" 17 | s.executables << 'dockrails' 18 | s.add_dependency 'commander', '~> 4.2' 19 | s.add_dependency 'docker-sync', '~> 0.4.0' 20 | 21 | s.add_development_dependency('rspec', '~> 3.2') 22 | s.add_development_dependency('rake') 23 | s.add_development_dependency('pry') 24 | s.add_development_dependency('fakefs') 25 | s.add_development_dependency('coveralls') 26 | end 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Guillaume Montard 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | dockrails (1.1.0) 5 | commander (~> 4.2) 6 | docker-sync (~> 0.4.0) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | backticks (1.0.0) 12 | coderay (1.1.1) 13 | commander (4.4.3) 14 | highline (~> 1.7.2) 15 | coveralls (0.8.20) 16 | json (>= 1.8, < 3) 17 | simplecov (~> 0.14.1) 18 | term-ansicolor (~> 1.3) 19 | thor (~> 0.19.4) 20 | tins (~> 1.6) 21 | daemons (1.2.4) 22 | diff-lcs (1.3) 23 | docile (1.1.5) 24 | docker-compose (1.1.7) 25 | backticks (~> 1.0) 26 | docker-sync (0.4.6) 27 | daemons (~> 1.2, >= 1.2.3) 28 | docker-compose (~> 1.0, >= 1.0.2) 29 | dotenv (~> 2.1, >= 2.1.1) 30 | gem_update_checker (~> 0.2.0, >= 0.2.0) 31 | os 32 | terminal-notifier (= 1.6.3) 33 | thor (~> 0.19, >= 0.19.0) 34 | dotenv (2.2.1) 35 | fakefs (0.11.0) 36 | gem_update_checker (0.2.0) 37 | highline (1.7.8) 38 | json (2.0.4) 39 | method_source (0.8.2) 40 | os (1.0.0) 41 | pry (0.10.4) 42 | coderay (~> 1.1.0) 43 | method_source (~> 0.8.1) 44 | slop (~> 3.4) 45 | rake (12.0.0) 46 | rspec (3.5.0) 47 | rspec-core (~> 3.5.0) 48 | rspec-expectations (~> 3.5.0) 49 | rspec-mocks (~> 3.5.0) 50 | rspec-core (3.5.4) 51 | rspec-support (~> 3.5.0) 52 | rspec-expectations (3.5.0) 53 | diff-lcs (>= 1.2.0, < 2.0) 54 | rspec-support (~> 3.5.0) 55 | rspec-mocks (3.5.0) 56 | diff-lcs (>= 1.2.0, < 2.0) 57 | rspec-support (~> 3.5.0) 58 | rspec-support (3.5.0) 59 | simplecov (0.14.1) 60 | docile (~> 1.1.0) 61 | json (>= 1.8, < 3) 62 | simplecov-html (~> 0.10.0) 63 | simplecov-html (0.10.0) 64 | slop (3.6.0) 65 | term-ansicolor (1.5.0) 66 | tins (~> 1.0) 67 | terminal-notifier (1.6.3) 68 | thor (0.19.4) 69 | tins (1.13.2) 70 | 71 | PLATFORMS 72 | ruby 73 | 74 | DEPENDENCIES 75 | coveralls 76 | dockrails! 77 | fakefs 78 | pry 79 | rake 80 | rspec (~> 3.2) 81 | 82 | BUNDLED WITH 83 | 1.14.5 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Gem Version](https://badge.fury.io/rb/dockrails.svg)](https://badge.fury.io/rb/dockrails) [![Build Status](https://travis-ci.org/gmontard/dockrails.svg?branch=master)](https://travis-ci.org/gmontard/dockrails) [![Coverage Status](https://coveralls.io/repos/github/gmontard/dockrails/badge.svg?branch=master)](https://coveralls.io/github/gmontard/dockrails?branch=master) 2 | 3 | ![](http://i.imgur.com/GpOR4F5.png) 4 | 5 | Simple CLI to Generate and Run a Rails environment with Docker! 6 | --- 7 | (using Docker, Docker-Compose and Docker-Sync behind the scenes) 8 | 9 | ### About 10 | 11 | Many of us have been struggling to setup a **usable** and **efficient** docker development environment for Rails App 12 | 13 | This Gem include a CLI that generate your docker environment then provide a simple command to run it. 14 | 15 | Web/Job containers **sync code base from your Rails App in realtime**, letting you code on you Mac and run in the same time everything from the container 16 | 17 | Bundler Gems, DB and Redis data are **persisted across restart** and you can use **ByeBug or Pry out of the box** easily. 18 | 19 | Currently the CLI offers a Docker environment with the option of: 20 | - PGSQL or MYSQL Database 21 | - Redis Database 22 | - Web and Job (Sidekiq) container 23 | 24 | You can expand this scope very easily by modifying the output docker files generated. 25 | 26 | [![asciicast](https://asciinema.org/a/26tls9e5bh860ai91d7grkkf6.png)](https://asciinema.org/a/26tls9e5bh860ai91d7grkkf6) 27 | 28 | ### Install 29 | 30 | ```gem install dockrails``` 31 | 32 | ### Requirements 33 | 34 | - [Docker Toolbox](https://www.docker.com/products/docker-toolbox) 35 | - ```brew install unison``` 36 | 37 | ### Commands 38 | 39 | Create a folder on top of your Rails App, add your App inside it, then run: 40 | - ```dockrails init``` 41 | 42 | *Answer the different questions to build your docker environment and then you are ready to run it!* 43 | 44 | Start the containers: 45 | - ```dockrails start``` 46 | 47 | Stop/Remove the containers: 48 | - ```dockrails clean``` 49 | 50 | Show live tail of logs: 51 | - ```dockrails logs``` 52 | 53 | Show live tail of logs for a container: 54 | - ```dockrails logs web``` 55 | 56 | Build/Rebuild the docker image: 57 | - ```dockrails build``` 58 | 59 | Restart a container: 60 | - ```dockrails restart CONTAINER``` 61 | 62 | Run a command inside a container: 63 | - ```dockrails run CONTAINER COMMAND``` (ex: dockrails run web bundle install) 64 | 65 | Attach TTY to a container (ex: for debugging with ByeBug or Pry): 66 | - ```dockrails attach CONTAINER``` 67 | 68 | 69 | ### Folder structure 70 | 71 | ``` 72 | my-docker-rails-env/ 73 | bundle/ 74 | data/ 75 | sql/ 76 | redis/ 77 | Dockerfile 78 | docker-compose.yml 79 | docker-sync.yml 80 | YOUR_RAILS_APP/ 81 | ``` 82 | -------------------------------------------------------------------------------- /bin/dockrails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'rubygems' 4 | require 'commander/import' 5 | require 'dockrails' 6 | 7 | program :name, 'dockrails' 8 | program :version, Dockrails::VERSION 9 | program :description, 'CLI to Generate and Run a Rails environment with Docker' 10 | program :help_formatter, Commander::HelpFormatter::TerminalCompact 11 | program :help_paging, false 12 | 13 | default_command :help 14 | 15 | command :init do |c| 16 | c.syntax = 'dockrails init' 17 | c.summary = 'Initialize your Rails Docker environment' 18 | c.description = '' 19 | c.example 'description', 'dockrails init' 20 | c.action do |args, options| 21 | 22 | existing_files = ["Dockerfile", "docker-compose.yml", "docker-sync.yml"].map { |file| File.exist?(file) } 23 | if existing_files.include?(true) 24 | say "<%= color 'We detected an existing Docker install in this folder', YELLOW %>" 25 | answer = agree "<%= color 'Do you want to continue and override it? (yes|no)', BOLD %>" 26 | exit if !answer 27 | end 28 | 29 | rails_app = ask "\n<%= color 'Type the name of your Rails App folder', BOLD %>" 30 | if !system("find #{rails_app} > /dev/null") 31 | say "\n<%= color 'Unable to find your Rails application folder in the current path, please try again', RED %>" 32 | say "<%= color 'If you want to create a new Rails app: rails new my_app -B -d postgresql', UNDERLINE %>" 33 | exit 34 | end 35 | 36 | generate = Dockrails::Generate.new(app_path: rails_app) 37 | 38 | (generate.check_system_requirements).each do |k,v| 39 | if v == false 40 | say "\n<%= color 'Please install #{k} in order to continue', RED%>" 41 | exit 42 | end 43 | end 44 | 45 | generate.configure 46 | generate.create_folders 47 | generate.create_dockerfile 48 | generate.create_start_script 49 | generate.create_docker_compose 50 | generate.create_docker_sync 51 | 52 | say "\n<%= color 'Successful dockerization! 53 | Please take a look at the different files generated: 54 | - Dockerfile 55 | - docker-compose.yml 56 | - docker-sync.yml', GREEN%>" 57 | 58 | say "\nUpdate your \"database.yml\" file: 59 | development: 60 | <<: *default 61 | database: <%%= ENV['DB_USER'] %> 62 | username: <%%= ENV['DB_USER'] %> 63 | password: <%%= ENV['DB_PASSWORD'] %> 64 | host: db" 65 | 66 | say "\nStart your dockerized Rails Environement: dockrails start" 67 | say "Familiarize yourself with the different commands: dockrails --help" 68 | end 69 | end 70 | 71 | command :start do |c| 72 | c.syntax = 'dockrails start' 73 | c.summary = 'Start the docker containers' 74 | c.description = '' 75 | c.example 'description', 'dockrails start' 76 | c.action do |args, options| 77 | system("docker-sync start ; docker-compose up") 78 | end 79 | end 80 | 81 | command :clean do |c| 82 | c.syntax = 'dockrails clean' 83 | c.summary = 'Stop the containers' 84 | c.description = '' 85 | c.example 'description', 'dockrails clean' 86 | c.action do |args, options| 87 | system("docker-compose stop ; docker-sync clean") 88 | end 89 | end 90 | alias_command :stop, :'clean' 91 | 92 | command :build do |c| 93 | c.syntax = 'dockrails build' 94 | c.summary = 'Build or rebuild the services' 95 | c.description = '' 96 | c.example 'description', 'dockrails build' 97 | c.action do |args, options| 98 | system("docker-compose build") 99 | end 100 | end 101 | 102 | command :logs do |c| 103 | c.syntax = 'dockrails logs [container]' 104 | c.summary = 'Access containers logs' 105 | c.description = '' 106 | c.example 'description', 'dockrails logs web' 107 | c.action do |args, options| 108 | if args.size < 1 109 | system("docker-compose logs --tail=20 -f") 110 | else 111 | system("docker-compose logs --tail=20 -f #{args.join(" ")}") 112 | end 113 | end 114 | end 115 | 116 | command :run do |c| 117 | c.syntax = 'dockrails run [container] [command]' 118 | c.summary = 'Run a command on a specific container' 119 | c.description = '' 120 | c.example 'description', 'dockrails run [web|job|db|redis] bundle install' 121 | c.action do |args, options| 122 | if args.size < 2 123 | say "\n<%= color 'Please specify a container and command to run, ex: dockrails run web bundle install', RED%>" 124 | else 125 | system("docker-compose run #{args.join(" ")}") 126 | end 127 | end 128 | end 129 | 130 | command :restart do |c| 131 | c.syntax = 'dockrails restart [container]' 132 | c.summary = 'Restart a specific container' 133 | c.description = '' 134 | c.example 'description', 'dockrails restart web' 135 | c.action do |args, options| 136 | if args.size < 1 137 | say "\n<%= color 'Please specify a container, ex: dockrails restart web', RED%>" 138 | else 139 | system("docker-compose restart #{args.join(" ")}") 140 | end 141 | end 142 | end 143 | 144 | command :attach do |c| 145 | c.syntax = 'dockrails attach [container]' 146 | c.summary = 'Attach TTY to a specific container (usefull for ByeBug or Pry)' 147 | c.description = '' 148 | c.example 'description', 'dockrails attach web' 149 | c.action do |args, options| 150 | if args.size < 1 151 | say "\n<%= color 'Please specify a container, ex: dockrails attach web', RED%>" 152 | else 153 | system("docker attach --detach-keys ctrl-c $(docker-compose ps -q #{args[0]})") 154 | end 155 | end 156 | end 157 | 158 | command :update do |c| 159 | c.syntax = 'dockrails update' 160 | c.summary = 'Update to latest Dockrails version' 161 | c.description = '' 162 | c.example 'description', 'dockrails update' 163 | c.action do |args, options| 164 | system("gem update dockrails") 165 | end 166 | end 167 | -------------------------------------------------------------------------------- /spec/generate_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'dockrails/generate' 3 | 4 | 5 | class Dummy 6 | def command_line 7 | system("ls") 8 | end 9 | end 10 | 11 | describe Dockrails::Generate do 12 | 13 | before(:all) do 14 | #$stderr = StringIO.new 15 | #mock_terminal 16 | 17 | @app_path = "rails_app" 18 | @generate = Dockrails::Generate.new(app_path: @app_path) 19 | end 20 | 21 | describe ".check_system_requirements" do 22 | it "should test for Unison, docker, docker-compose and docker-sync binaries" do 23 | values = @generate.check_system_requirements.map{|k,v| k} 24 | expect(values).to eql ["unison", "docker", "docker-sync", "docker-compose"] 25 | end 26 | 27 | context "when all requirements are met" do 28 | it { 29 | allow(@generate).to receive(:binary_present?).with(binary: "unison") { true } 30 | allow(@generate).to receive(:binary_present?).with(binary: "docker") { true } 31 | allow(@generate).to receive(:binary_present?).with(binary: "docker-sync") { true } 32 | allow(@generate).to receive(:binary_present?).with(binary: "docker-compose") { true } 33 | 34 | expect(@generate.check_system_requirements).to be_kind_of Hash 35 | } 36 | end 37 | 38 | context "when NOT all requirements are met" do 39 | it { 40 | allow(@generate).to receive(:binary_present?).with(binary: "unison") { false } 41 | allow(@generate).to receive(:binary_present?).with(binary: "docker") { true } 42 | allow(@generate).to receive(:binary_present?).with(binary: "docker-sync") { true } 43 | allow(@generate).to receive(:binary_present?).with(binary: "docker-compose") { true } 44 | 45 | expect(@generate.check_system_requirements).to be_kind_of Hash 46 | } 47 | end 48 | end 49 | 50 | describe ".configure" do 51 | end 52 | 53 | context "once it's configured" do 54 | 55 | before(:all) do 56 | @app_path = "rails_app" 57 | 58 | @config_with_redis = { 59 | ruby: "latest", 60 | db: :pgsql, 61 | db_name: "rails", 62 | db_user: "rails", 63 | db_password: "rails", 64 | redis: true, 65 | sidekiq: true 66 | } 67 | 68 | @config_without_redis = { 69 | ruby: "latest", 70 | db: :pgsql, 71 | db_name: "rails", 72 | db_user: "rails", 73 | db_password: "rails", 74 | redis: false, 75 | sidekiq: false 76 | } 77 | 78 | @config_with_mysql = { 79 | ruby: "latest", 80 | db: :mysql, 81 | db_name: "rails", 82 | db_user: "rails", 83 | db_password: "rails", 84 | redis: false, 85 | sidekiq: false 86 | } 87 | 88 | @generate_with_redis = Dockrails::Generate.new(app_path: @app_path, config: @config_with_redis) 89 | @generate_without_redis = Dockrails::Generate.new(app_path: @app_path, config: @config_without_redis) 90 | @generate_with_mysql = Dockrails::Generate.new(app_path: @app_path, config: @config_with_mysql) 91 | end 92 | 93 | describe ".create_folders" do 94 | context "When Redis is not set" do 95 | it "should create the data and data/sql folders" do 96 | allow(FileUtils).to receive(:mkdir_p).with("data") 97 | allow(FileUtils).to receive(:mkdir_p).with("data/sql") 98 | @generate_without_redis.create_folders 99 | end 100 | end 101 | 102 | context "When Redis is set" do 103 | it "should create the data, data/sql and data/redis folders" do 104 | allow(FileUtils).to receive(:mkdir_p).with("data") 105 | allow(FileUtils).to receive(:mkdir_p).with("data/sql") 106 | allow(FileUtils).to receive(:mkdir_p).with("data/redis") 107 | @generate_with_redis.create_folders 108 | end 109 | end 110 | end 111 | 112 | describe ".create_dockerfile" do 113 | it "Should create a Dockerfile" do 114 | allow(File).to receive(:open).with("Dockerfile", 'w').once 115 | @generate_with_redis.create_dockerfile 116 | end 117 | end 118 | 119 | describe ".create_docker_compose" do 120 | it "Should create a docker-compose.yml file" do 121 | allow(File).to receive(:open).with("docker-compose.yml", 'w').once 122 | @generate_with_redis.create_docker_compose 123 | end 124 | 125 | context "When PGSQL is set" do 126 | it "Should contain the Pgsql config" do 127 | @generate_with_redis.create_docker_compose 128 | expect(File.read("docker-compose.yml")).to match /postgres/i 129 | end 130 | end 131 | 132 | context "When MySQL is set" do 133 | it "Should contain the Mysql config" do 134 | @generate_with_mysql.create_docker_compose 135 | expect(File.read("docker-compose.yml")).to match /mysql/i 136 | end 137 | end 138 | 139 | context "When Redis is not set" do 140 | it "Should not contain a Redis node" do 141 | @generate_without_redis.create_docker_compose 142 | expect(File.read("docker-compose.yml")).not_to match /redis/i 143 | end 144 | 145 | it "Should not contain a Sidekiq node" do 146 | @generate_without_redis.create_docker_compose 147 | expect(File.read("docker-compose.yml")).not_to match /sidekiq/i 148 | end 149 | end 150 | 151 | context "When Redis & Sidekiq are set" do 152 | it "Should contain a Redis node" do 153 | @generate_with_redis.create_docker_compose 154 | expect(File.read("docker-compose.yml")).to match /redis/i 155 | end 156 | 157 | it "Should contain a Sidekiq node" do 158 | @generate_with_redis.create_docker_compose 159 | expect(File.read("docker-compose.yml")).to match /sidekiq/i 160 | end 161 | end 162 | end 163 | 164 | describe ".create_docker_sync" do 165 | it "Should create a docker-sync.yml file" do 166 | allow(File).to receive(:open).with("docker-sync.yml", 'w').once 167 | @generate_with_redis.create_docker_sync 168 | end 169 | end 170 | 171 | describe ".create_start_script" do 172 | it "Should create a scripts folder and start-dev.sh file" do 173 | allow(File).to receive(:open).with("#{@app_path}/scripts/start-dev.sh", 'w').once 174 | allow(FileUtils).to receive(:mkdir_p).with("#{@app_path}/scripts").once 175 | @generate_with_redis.create_start_script 176 | end 177 | end 178 | end 179 | 180 | end 181 | -------------------------------------------------------------------------------- /lib/dockrails/generate.rb: -------------------------------------------------------------------------------- 1 | module Dockrails 2 | class Generate 3 | 4 | def initialize(app_path:, config: {}) 5 | @app_path = app_path 6 | @config = config 7 | end 8 | 9 | def check_system_requirements 10 | requirement = Hash.new 11 | 12 | ["unison", "docker", "docker-sync", "docker-compose"].each do |binary| 13 | requirement[binary] = binary_present?(binary: binary) 14 | end 15 | 16 | return requirement 17 | end 18 | 19 | def binary_present?(binary:) 20 | system("which #{binary} > /dev/null") 21 | end 22 | 23 | def configure 24 | @config[:ruby] = choose("\n<%= color 'Choose a ruby version?', BOLD%>", "latest", "2.4", "2.3", "2.2") 25 | @config[:db] = choose("\n<%= color 'Choose a DB Engine', BOLD%>", :pgsql, :mysql) 26 | @config[:db_name] = ask "\n<%= color 'Choose a database name', BOLD%>" 27 | @config[:db_user] = ask "\n<%= color 'Choose a database username', BOLD%>" 28 | @config[:db_password] = ask "\n<%= color 'Choose a database password', BOLD%>" 29 | @config[:redis] = agree("\n<%= color 'Do you need a Redis DB? (yes|no)', BOLD%>") 30 | @config[:sidekiq] = agree("\n<%= color 'Do you need a SideKiq Container? (yes|no)', BOLD%>") if @config[:redis] 31 | 32 | say "\nSummary: 33 | - Ruby version: #{@config[:ruby]} 34 | - DB Engine: #{@config[:db]} 35 | - DB Name: #{@config[:db_name]} 36 | - DB Username: #{@config[:db_user]} 37 | - DB Password: #{@config[:db_password]} 38 | - Redis? #{@config[:redis]} 39 | - Job Container? #{@config[:sidekiq] ||= false}" 40 | 41 | user_agree = agree("\n<%= color 'Is this correct? (yes|no)', BOLD%>") 42 | 43 | unless user_agree 44 | configure 45 | end 46 | 47 | return(@config) 48 | end 49 | 50 | def create_folders 51 | FileUtils.rm_rf('data') 52 | FileUtils.mkdir_p('data/sql') 53 | FileUtils.mkdir_p('data/redis') if @config[:redis] 54 | end 55 | 56 | def create_dockerfile 57 | File.open("Dockerfile", 'w') do |f| 58 | f.write("FROM ruby:#{@config[:ruby]} 59 | RUN apt-get update && apt-get install -y \ 60 | build-essential \ 61 | wget \ 62 | git-core \ 63 | libxml2 \ 64 | libxml2-dev \ 65 | libxslt1-dev \ 66 | nodejs \ 67 | imagemagick \ 68 | libmagickcore-dev \ 69 | libmagickwand-dev \ 70 | libpq-dev \ 71 | && rm -rf /var/lib/apt/lists/* 72 | 73 | RUN mkdir /app 74 | RUN mkdir -p /root/.ssh/ 75 | 76 | WORKDIR /app 77 | 78 | RUN ssh-keyscan -H github.com >> ~/.ssh/known_hosts 79 | ENV GEM_HOME /bundle 80 | ENV PATH $GEM_HOME/bin:$PATH 81 | ENV BUNDLE_PATH /bundle 82 | ENV BUNDLE_BIN /bundle/ 83 | RUN gem install bundler -v '1.10.6' \ 84 | && bundle config --global path \"$GEM_HOME\" \ 85 | && bundle config --global bin \"$GEM_HOME/bin\"") 86 | end 87 | end 88 | 89 | def create_start_script 90 | FileUtils.mkdir_p("#{@app_path}/scripts") 91 | File.open("#{@app_path}/scripts/start-dev.sh", "w") do |f| 92 | f.write("#!/bin/bash 93 | bundle check || bundle install 94 | rm -rf tmp/pids/* && bundle exec rails server --port 3000 --binding 0.0.0.0") 95 | end 96 | end 97 | 98 | def create_docker_compose 99 | File.open("docker-compose.yml", 'w') do |f| 100 | f.write "version: '2'\n" 101 | f.write "services:\n" 102 | f.write " db:\n" 103 | 104 | case @config[:db] when :mysql 105 | f.write " image: mysql\n" 106 | f.write " volumes:\n" 107 | f.write " - ./data/sql:/var/lib/mysql\n" 108 | f.write " ports:\n" 109 | f.write " - \"3306:3306\"\n" 110 | f.write " environment:\n" 111 | f.write " MYSQL_DATABASE: #{@config[:db_name]}\n" 112 | f.write " MYSQL_USER: #{@config[:db_user]}\n" 113 | f.write " MYSQL_PASSWORD: #{@config[:db_password]}\n" 114 | f.write " MYSQL_ROOT_PASSWORD: #{@config[:db_password]}\n" 115 | when :pgsql 116 | f.write " image: postgres\n" 117 | f.write " volumes:\n" 118 | f.write " - ./data/sql:/var/lib/postgresql/data\n" 119 | f.write " ports:\n" 120 | f.write " - \"5432:5432\"\n" 121 | f.write " environment:\n" 122 | f.write " POSTGRES_DB: #{@config[:db_name]}\n" 123 | f.write " POSTGRES_USER: #{@config[:db_user]}\n" 124 | f.write " POSTGRES_PASSWORD: #{@config[:db_password]}\n" 125 | end 126 | 127 | if @config[:redis] 128 | f.write "\n redis:\n" 129 | f.write " image: redis\n" 130 | f.write " volumes:\n" 131 | f.write " - ./data/redis:/data\n" 132 | f.write " ports:\n" 133 | f.write " - \"6379:6379\"\n" 134 | end 135 | 136 | f.write "\n web:\n" 137 | f.write " build: .\n" 138 | f.write " command: sh scripts/start-dev.sh\n" 139 | f.write " volumes:\n" 140 | f.write " - #{@app_path}-web-sync:/app:nocopy\n" 141 | f.write " - #{@app_path}-bundle-sync:/bundle:nocopy\n" 142 | f.write " - ./keys:/root/.ssh/\n" 143 | f.write " ports:\n" 144 | f.write " - \"3000:3000\"\n" 145 | f.write " environment:\n" 146 | f.write " REDIS_URL: redis://redis:6379\n" if @config[:redis] 147 | f.write " DB_USER: #{@config[:db_user]}\n" 148 | f.write " DB_PASSWORD: #{@config[:db_password]}\n" 149 | f.write " links:\n" 150 | f.write " - db\n" 151 | f.write " - redis\n" if @config[:redis] 152 | f.write " tty: true\n" 153 | f.write " stdin_open: true\n" 154 | 155 | if @config[:redis] && @config[:sidekiq] 156 | f.write "\n job:\n" 157 | f.write " build: .\n" 158 | f.write " command: bundle exec sidekiq -C config/sidekiq.yml\n" 159 | f.write " volumes:\n" 160 | f.write " - #{@app_path}-web-sync:/app:nocopy\n" 161 | f.write " - #{@app_path}-bundle-sync:/bundle:nocopy\n" 162 | f.write " - ./keys:/root/.ssh/\n" 163 | f.write " environment:\n" 164 | f.write " REDIS_URL: redis://redis:6379\n" 165 | f.write " links:\n" 166 | f.write " - db\n" 167 | f.write " - redis\n" 168 | end 169 | 170 | f.write "\nvolumes:\n" 171 | f.write " #{@app_path}-web-sync:\n" 172 | f.write " external: true\n" 173 | f.write " #{@app_path}-bundle-sync:\n" 174 | f.write " external: true\n" 175 | end 176 | end 177 | 178 | def create_docker_sync 179 | File.open("docker-sync.yml", 'w') do |f| 180 | f.write "version: '2'\n" 181 | f.write "syncs:\n" 182 | f.write " #{@app_path}-web-sync:\n" 183 | f.write " src: './#{@app_path}'\n" 184 | f.write " #{@app_path}-bundle-sync:\n" 185 | f.write " src: './bundle'\n" 186 | end 187 | end 188 | end 189 | end 190 | --------------------------------------------------------------------------------