Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for continuous integration #51

Merged
merged 6 commits into from
Mar 4, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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

Expand Down
2 changes: 2 additions & 0 deletions circle.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
machine:
ruby:
version: 2.3.0
services:
- docker

dependencies:
override:
Expand Down
4 changes: 4 additions & 0 deletions lib/potassium/templates/application/assets/Dockerfile.ci
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FROM platanus/buildstep
EXPOSE 3000

RUN /exec bundle install --with test
14 changes: 12 additions & 2 deletions lib/potassium/templates/application/assets/README.md.erb
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
27 changes: 27 additions & 0 deletions lib/potassium/templates/application/assets/bin/cibuild.erb
Original file line number Diff line number Diff line change
@@ -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
16 changes: 16 additions & 0 deletions lib/potassium/templates/application/assets/circle.yml
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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)) %>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)) %>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
test:
build: .
dockerfile: Dockerfile.ci
working_dir: '/app'
environment:
RAILS_ENV: test
37 changes: 37 additions & 0 deletions lib/potassium/templates/application/helpers/docker-helpers.rb
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down
36 changes: 36 additions & 0 deletions lib/potassium/templates/application/recipes/ci.rb
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions lib/potassium/templates/application/template.rb
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -44,6 +45,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

Expand Down
14 changes: 14 additions & 0 deletions spec/features/ci_spec.rb
Original file line number Diff line number Diff line change
@@ -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
41 changes: 27 additions & 14 deletions spec/support/potassium_test_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down