From 616a7b08c48380d9f0470e8afac091e4f995138b Mon Sep 17 00:00:00 2001 From: Juan Ignacio Donoso Date: Thu, 3 Mar 2016 11:38:48 -0500 Subject: [PATCH 1/6] chore(): add alternative env variables for database connections --- .../application/assets/config/database_mysql.yml.erb | 4 ++-- .../application/assets/config/database_postgresql.yml.erb | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/potassium/templates/application/assets/config/database_mysql.yml.erb b/lib/potassium/templates/application/assets/config/database_mysql.yml.erb index f8efe980..1816ea45 100644 --- a/lib/potassium/templates/application/assets/config/database_mysql.yml.erb +++ b/lib/potassium/templates/application/assets/config/database_mysql.yml.erb @@ -3,8 +3,8 @@ development: &default database: <%= get(:underscorized_app_name) %>_development encoding: utf8 username: root - host: <%%= ENV["BOXEN_MYSQL_HOST"] || "127.0.0.1" %> - port: <%%= ENV["BOXEN_MYSQL_PORT"] || 3306 %> + host: <%%= ENV["BOXEN_MYSQL_HOST"] || ENV["MYSQL_HOST"] || "127.0.0.1" %> + port: <%%= ENV["BOXEN_MYSQL_PORT"] || ENV["MYSQL_PORT"] || 3306 %> min_messages: warning pool: <%%= Integer(ENV.fetch("DB_POOL", 5)) %> reaping_frequency: <%%= Integer(ENV.fetch("DB_REAPING_FREQUENCY", 10)) %> diff --git a/lib/potassium/templates/application/assets/config/database_postgresql.yml.erb b/lib/potassium/templates/application/assets/config/database_postgresql.yml.erb index 87342a09..a94f0ee5 100644 --- a/lib/potassium/templates/application/assets/config/database_postgresql.yml.erb +++ b/lib/potassium/templates/application/assets/config/database_postgresql.yml.erb @@ -2,8 +2,9 @@ development: &default adapter: postgresql database: <%= get(:underscorized_app_name) %>_development encoding: utf8 - host: <%%= ENV["BOXEN_POSTGRESQL_HOST"] || "127.0.0.1" %> - port: <%%= ENV["BOXEN_POSTGRESQL_PORT"] || 5432 %> + host: <%%= ENV["BOXEN_POSTGRESQL_HOST"] || ENV["POSTGRESQL_HOST"] || "127.0.0.1" %> + port: <%%= ENV["BOXEN_POSTGRESQL_PORT"] || ENV["POSTGRESQL_PORT"] || 5432 %> + username: <%%= ENV["POSTGRESQL_USER"] %> min_messages: warning pool: <%%= Integer(ENV.fetch("DB_POOL", 5)) %> reaping_frequency: <%%= Integer(ENV.fetch("DB_REAPING_FREQUENCY", 10)) %> From bec4fdb98020a8667915af7ee7493bac01ef8f93 Mon Sep 17 00:00:00 2001 From: Juan Ignacio Donoso Date: Thu, 3 Mar 2016 11:40:09 -0500 Subject: [PATCH 2/6] feat(rspec): adds support to defined arguments to the create_dummy_project method --- spec/support/potassium_test_helpers.rb | 41 +++++++++++++++++--------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/spec/support/potassium_test_helpers.rb b/spec/support/potassium_test_helpers.rb index 3fe9b3e1..92bf9a2a 100644 --- a/spec/support/potassium_test_helpers.rb +++ b/spec/support/potassium_test_helpers.rb @@ -9,10 +9,11 @@ def create_tmp_directory FileUtils.mkdir_p(tmp_path) end - def create_dummy_project + def create_dummy_project(arguments = {}) Dir.chdir(tmp_path) do Bundler.with_clean_env do - run_command("#{potassium_bin} create #{APP_NAME} #{bin_arguments}") + full_arguments = hash_to_arguments(default_arguments.merge(arguments)) + run_command("#{potassium_bin} create #{APP_NAME} #{full_arguments}") end end end @@ -44,18 +45,30 @@ def potassium_bin File.join(root_path, "bin", "potassium") end - def bin_arguments - [ - "--db=mysql", - "--lang=es", - "--no-devise", - "--no-admin", - "--no-pundit", - "--no-paperclip", - "--no-api", - "--no-heroku", - "--no-delayed-job" - ].join(" ") + def default_arguments + { + "db" => "mysql", + "lang" => "es", + "heroku" => false, + "admin" => false, + "pundit" => false, + "paperclip" => false, + "devise" => false, + "api" => false, + "delayed-job" => false + } + end + + def hash_to_arguments(hash) + hash.map do |key, value| + if value == true + "--#{key}" + elsif value == false + "--no-#{key}" + elsif value + "--#{key}=#{value}" + end + end.join(" ") end def root_path From 54f81407349adf7ef9b6aca111660c63b7cffd3c Mon Sep 17 00:00:00 2001 From: Juan Ignacio Donoso Date: Thu, 3 Mar 2016 11:41:47 -0500 Subject: [PATCH 3/6] feat(): adds continuous integration support when deploying to heroku --- circle.yml | 2 + .../application/assets/Dockerfile.ci | 4 ++ .../application/assets/bin/cibuild.erb | 27 ++++++++++++++ .../templates/application/assets/circle.yml | 16 ++++++++ .../application/assets/docker-compose.ci.yml | 6 +++ .../application/helpers/docker-helpers.rb | 37 +++++++++++++++++++ .../application/helpers/template-dsl.rb | 1 + .../templates/application/recipes/ci.rb | 36 ++++++++++++++++++ .../templates/application/template.rb | 1 + spec/features/ci_spec.rb | 14 +++++++ 10 files changed, 144 insertions(+) create mode 100644 lib/potassium/templates/application/assets/Dockerfile.ci create mode 100644 lib/potassium/templates/application/assets/bin/cibuild.erb create mode 100644 lib/potassium/templates/application/assets/circle.yml create mode 100644 lib/potassium/templates/application/assets/docker-compose.ci.yml create mode 100644 lib/potassium/templates/application/helpers/docker-helpers.rb create mode 100644 lib/potassium/templates/application/recipes/ci.rb create mode 100644 spec/features/ci_spec.rb diff --git a/circle.yml b/circle.yml index d0be8866..a1e96cd9 100644 --- a/circle.yml +++ b/circle.yml @@ -1,6 +1,8 @@ machine: ruby: version: 2.3.0 + services: + - docker dependencies: override: diff --git a/lib/potassium/templates/application/assets/Dockerfile.ci b/lib/potassium/templates/application/assets/Dockerfile.ci new file mode 100644 index 00000000..a3fc8c97 --- /dev/null +++ b/lib/potassium/templates/application/assets/Dockerfile.ci @@ -0,0 +1,4 @@ +FROM platanus/buildstep +EXPOSE 3000 + +RUN /exec bundle install --with test diff --git a/lib/potassium/templates/application/assets/bin/cibuild.erb b/lib/potassium/templates/application/assets/bin/cibuild.erb new file mode 100644 index 00000000..8fcc3f87 --- /dev/null +++ b/lib/potassium/templates/application/assets/bin/cibuild.erb @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +DOCKE_COMPOSE_ARGS="-f docker-compose.ci.yml run" + +<% if(selected?(:database, :mysql) || selected?(:database, :postgresql))-%> +function test_<%=get(:database).to_s%> { + docker-compose $DOCKE_COMPOSE_ARGS test sh -c 'nc -z $<%=get(:database).to_s.upcase%>_HOST $<%=get(:database).to_s.upcase%>_PORT' +} + +count=0 +# Chain tests together by using && +until ( test_<%=get(:database).to_s%> && echo "Services ready" ) +do + ((count++)) + if [ ${count} -gt 50 ] + then + echo "Services didn't become ready in time" + exit 1 + else + echo "Waiting for services to become ready..." + fi + sleep 0.2 +done +<% end-%> + +docker-compose $DOCKE_COMPOSE_ARGS test /exec bundle exec rake db:setup +docker-compose $DOCKE_COMPOSE_ARGS test /exec bundle exec rspec spec diff --git a/lib/potassium/templates/application/assets/circle.yml b/lib/potassium/templates/application/assets/circle.yml new file mode 100644 index 00000000..ec6603ee --- /dev/null +++ b/lib/potassium/templates/application/assets/circle.yml @@ -0,0 +1,16 @@ +machine: + services: + - docker + +dependencies: + override: + - docker-compose -f docker-compose.ci.yml pull + - docker-compose -f docker-compose.ci.yml build test + +database: + override: + - echo "Skipping database" + +test: + override: + - bin/cibuild diff --git a/lib/potassium/templates/application/assets/docker-compose.ci.yml b/lib/potassium/templates/application/assets/docker-compose.ci.yml new file mode 100644 index 00000000..3da1f9c2 --- /dev/null +++ b/lib/potassium/templates/application/assets/docker-compose.ci.yml @@ -0,0 +1,6 @@ +test: + build: . + dockerfile: Dockerfile.ci + working_dir: '/app' + environment: + RAILS_ENV: test diff --git a/lib/potassium/templates/application/helpers/docker-helpers.rb b/lib/potassium/templates/application/helpers/docker-helpers.rb new file mode 100644 index 00000000..064ddfa8 --- /dev/null +++ b/lib/potassium/templates/application/helpers/docker-helpers.rb @@ -0,0 +1,37 @@ +class DockerHelpers + def initialize(compose_path) + @compose_path = compose_path + @compose = YAML.load(File.read(compose_path)) + end + + def add_link(target_service, linked_service) + service = @compose[target_service] + unless service['links'].is_a? Array + service['links'] = [] + end + service['links'].push(linked_service) + save + end + + def add_env(target_service, variable_key, variable_value) + service = @compose[target_service] + unless service['environment'].is_a? Hash + service['environment'] = {} + end + service['environment'][variable_key] = variable_value + save + end + + def add_service(name, definition) + service = {} + service[name] = YAML.load(definition) + @compose.merge!(service) + save + end + + private + + def save + File.open(@compose_path, 'w') { |f| f.write @compose.to_yaml } + end +end diff --git a/lib/potassium/templates/application/helpers/template-dsl.rb b/lib/potassium/templates/application/helpers/template-dsl.rb index a261a4b4..20b60ce0 100644 --- a/lib/potassium/templates/application/helpers/template-dsl.rb +++ b/lib/potassium/templates/application/helpers/template-dsl.rb @@ -4,6 +4,7 @@ def self.extend_dsl(object, source_path: __FILE__) require_relative './variable-helpers' require_relative './environment-helpers' require_relative './gem-helpers' + require_relative './docker-helpers' require_relative './callback-helpers' require_relative './answer-helpers' diff --git a/lib/potassium/templates/application/recipes/ci.rb b/lib/potassium/templates/application/recipes/ci.rb new file mode 100644 index 00000000..b7a1d1b0 --- /dev/null +++ b/lib/potassium/templates/application/recipes/ci.rb @@ -0,0 +1,36 @@ +if get(:heroku) + copy_file 'assets/Dockerfile.ci', 'Dockerfile.ci' + copy_file 'assets/circle.yml', 'circle.yml' + + template 'assets/bin/cibuild.erb', 'bin/cibuild' + run "chmod a+x bin/cibuild" + + copy_file 'assets/docker-compose.ci.yml', 'docker-compose.ci.yml' + + compose = DockerHelpers.new('docker-compose.ci.yml') + + if selected?(:database, :mysql) + service = <<-YAML + image: "mysql:5.6.23" + environment: + MYSQL_ALLOW_EMPTY_PASSWORD: 'true' + YAML + compose.add_service("mysql", service) + compose.add_link('test', 'mysql') + compose.add_env('test', 'MYSQL_HOST', 'mysql') + compose.add_env('test', 'MYSQL_PORT', '3306') + + elsif selected?(:database, :postgresql) + service = <<-YAML + image: "postgres:9.4.5" + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: '' + YAML + compose.add_service("postgresql", service) + compose.add_link('test', 'postgresql') + compose.add_env('test', 'POSTGRESQL_USER', 'postgres') + compose.add_env('test', 'POSTGRESQL_HOST', 'postgresql') + compose.add_env('test', 'POSTGRESQL_PORT', '5432') + end +end diff --git a/lib/potassium/templates/application/template.rb b/lib/potassium/templates/application/template.rb index 9e3e1afb..e09bd5c9 100644 --- a/lib/potassium/templates/application/template.rb +++ b/lib/potassium/templates/application/template.rb @@ -44,6 +44,7 @@ eval_file "recipes/api.rb" eval_file "recipes/rack-cors.rb" eval_file "recipes/paperclip.rb" + eval_file "recipes/ci.rb" eval_file "recipes/cleanup.rb" end diff --git a/spec/features/ci_spec.rb b/spec/features/ci_spec.rb new file mode 100644 index 00000000..c0448cc6 --- /dev/null +++ b/spec/features/ci_spec.rb @@ -0,0 +1,14 @@ +require "spec_helper" +require "rubocop" + +RSpec.describe "A new project" do + before(:all) do + drop_dummy_database + remove_project_directory + create_dummy_project("heroku" => true) + end + + it "correctly runs continous integration" do + expect { on_project { `bin/cibuild` } }.to_not output.to_stderr + end +end From b1e7a7f5617503f1553c3ebdbc2fa019ef286e2c Mon Sep 17 00:00:00 2001 From: Juan Ignacio Donoso Date: Thu, 3 Mar 2016 11:58:22 -0500 Subject: [PATCH 4/6] docs(): updates readme and changelog --- CHANGELOG.md | 14 +++++++++++--- README.md | 3 +++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29efdf69..330d6241 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,18 @@ Features: - Use Puma instead of unicorn - Gets ruby version from http://ruby.platan.us/latest - Adds the install command - - Adds heroku buildpack support using the [multi buildpack](http://github.com/ddollar/heroku-buildpack-multi) + - Adds heroku buildpack support using the [multi buildpack](multi-buildpack) - Adds `rack-timeout` to prevent long running requests - - Adds [deploy-tasks buildpack](http://github.com/gunpowderlabs/buildpack-ruby-rake-deploy-tasks) mainly to perform migrations, [#39](http://github.com/platanus/potassium/pull/39) - - Add Delayed Jobs to handle background processes [#41] + - Adds [deploy-tasks buildpack](deploy-tasks) mainly to perform migrations, [#39] + - Add Delayed Jobs to handle background processes, [#41] + - Adds continuous integration using [CircleCI](https://circleci.com) + and docker, [#51] + +[multi-buildpack]: http://github.com/ddollar/heroku-buildpack-multi +[deploy-tasts]: http://github.com/gunpowderlabs/buildpack-ruby-rake-deploy-tasks +[#39]: http://github.com/platanus/potassium/pull/39 +[#41]: http://github.com/platanus/potassium/pull/41 +[#51]: http://github.com/platanus/potassium/pull/51 ## 1.3 diff --git a/README.md b/README.md index ab6bd0e1..10afd007 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,8 @@ When you choose to deploy to heroku a few extra things are added for the project to configure the app to log to standard out, which is how [Heroku's logging][heroku-logging] works. - Adds a [Procfile][procfile] to define the processes to run in heroku +- Setup continuous integration in [CircleCI](circle-ci) to run tests. It use + docker to maintain better parity between testing and production environments - Adds a `.buildpacks` file with the default buildpacks to use. It use the following buildpacks: @@ -97,6 +99,7 @@ When you choose to deploy to heroku a few extra things are added for the project [heroku-buildpack-ruby]: http://github.com/heroku/heroku-buildpack-ruby [heroku-buildpack-multi]: http://github.com/ddollar/heroku-buildpack-multi [buildpack-deploy-tasks]: http://github.com/gunpowderlabs/buildpack-ruby-rake-deploy-tasks +[circle-ci]: https://circleci.com ## Contributing From 144901aa621d19befc74be23ddb3a7759029dac5 Mon Sep 17 00:00:00 2001 From: Juan Ignacio Donoso Date: Thu, 3 Mar 2016 12:08:40 -0500 Subject: [PATCH 5/6] docs(): adds CI section to the generated readme --- .../templates/application/assets/README.md.erb | 12 +++++++++++- lib/potassium/templates/application/template.rb | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/potassium/templates/application/assets/README.md.erb b/lib/potassium/templates/application/assets/README.md.erb index 22d25934..4191faf1 100644 --- a/lib/potassium/templates/application/assets/README.md.erb +++ b/lib/potassium/templates/application/assets/README.md.erb @@ -13,8 +13,8 @@ Assuming you've just cloned the repo: ## External services TODO: add external services here -## Deployment <% if selected?(:heroku) %> +## Deployment This project is pre-configured to be (easily) deployed to Heroku servers, but needs you to have the Potassium binary installed. If you don't, then run: ```shell $ gem install potassium @@ -43,6 +43,16 @@ Remember to connect each stage to the corresponding branch: 2. Production -> Production That's it. You should already have a running app and each time you push to the corresponding branch, the system will (hopefully) update accordingly. + +## Continuous Integrations + +The project is setup to run tests +in [CircleCI](https://circleci.com/gh/platanus/<%= get(:dasherized_app_name)%>/tree/master) + +You can also run the test locally simulating the production environment using docker. +Just make sure you have docker installed and run: + + bin/cibuild <% end-%> ## Internal dependencies diff --git a/lib/potassium/templates/application/template.rb b/lib/potassium/templates/application/template.rb index e09bd5c9..a2827be7 100644 --- a/lib/potassium/templates/application/template.rb +++ b/lib/potassium/templates/application/template.rb @@ -1,6 +1,7 @@ set :app_name, @app_name set :titleized_app_name, get(:app_name).titleize set :underscorized_app_name, get(:app_name).underscore +set :dasherized_app_name, get(:app_name).dasherize run_action(:cleaning) do clean_gemfile From 4d83b095a3e17196695dd2fcc0f31cf7cb58a012 Mon Sep 17 00:00:00 2001 From: Juan Ignacio Donoso Date: Thu, 3 Mar 2016 16:42:04 -0500 Subject: [PATCH 6/6] docs(): adds circleci status badge to the generated readme --- lib/potassium/templates/application/assets/README.md.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/potassium/templates/application/assets/README.md.erb b/lib/potassium/templates/application/assets/README.md.erb index 4191faf1..425c46ee 100644 --- a/lib/potassium/templates/application/assets/README.md.erb +++ b/lib/potassium/templates/application/assets/README.md.erb @@ -1,4 +1,4 @@ -# <%= get(:titleized_app_name) %> +# <%= get(:titleized_app_name) %> <% if selected?(:heroku) %>[![Circle CI](https://circleci.com/gh/platanus/<%= get(:dasherized_app_name)%>.svg?style=svg)](https://circleci.com/gh/platanus/<%= get(:dasherized_app_name)%>)<% end-%> This is a Rails application, initially generated using [Potassium](https://github.com/platanus/potassium) by Platanus. ## Local installation