Awesome
Roadshow
Summary
Roadshow is a tool that uses Docker and Docker Compose to make it easy to test your library or application against different sets of dependencies without relying on an external CI service.
Setup
(Note: you have to have working docker
and docker-compose
executables on
your PATH
to use Roadshow. On a Mac, you can install them using
Homebrew-Cask.)
Roadshow's configuration is specified in a YAML file which lives at the top
level of your project and is named scenarios.yml
.
To generate a skeleton for this file, use the init
command:
roadshow init
Here's an example of a basic scenarios.yml
file:
# Based on the name of your working directory by default. This will be
# prepended to the generated Docker image names created by Docker Compose.
project: someprojectname
# This configuration is shared by all of your scenarios, except where they
# override it. The format is identical to an individual scenario.
#
# Anywhere in this block, you can use the placeholder "{{scenario_name}}"
# to stand in for the name of each individual scenario.
shared:
# Specify the value to pass into FROM in the Dockerfile (i.e.,
# what image to use as a starting point for this scenario).
from: bash
# Specify the value to pass into CMD in the Dockerfile.
cmd: "echo 'default command' && echo $ENV_VAR"
# The individual scenarios.
scenarios:
one:
# Configuration for the main service in Docker Compose. Extra services
# aren't supported yet.
service:
environment:
ENV_VAR: scenario one
two:
cmd: "echo 'overridden command' && echo $ENV_VAR"
service:
environment:
ENV_VAR: scenario two
Usage
Once you've set up your scenarios.yml
file, you can generate Dockerfiles and
Docker Compose files into a subdirectory called scenarios
:
roadshow generate
Once you've done that, you can easily run all of the scenarios' default commands at once:
roadshow run
To run an individual scenario's default command, use the -s
or --scenario
option:
roadshow run -s rails32
To run a non-default command across all scenarios or an individual scenario,
pass extra arguments, optionally preceded by --
to avoid any ambiguity:
roadshow run rails console
roadshow run -s rails32 -- rails console
In all of these cases, if any of the individual commands exit with a non-zero status then Roadshow will too.
If you want to clean up after yourself, you can remove all containers, images, and volumes created by Roadshow for the current project:
roadshow clean
Example: Testing a Ruby library
If you have a Ruby library that integrates with other gems, it can be hard to
test it across various versions of Ruby and your dependencies. A
scenarios.yml
file for this situation might look like this:
project: my_cool_gem
shared:
cmd: "bundle install && bundle exec rake"
service:
volumes:
- bundle_{{scenario_name}}:/usr/local/bundle
environment:
BUNDLE_GEMFILE: scenarios/{{scenario_name}}.gemfile
volumes:
bundle_{{scenario_name}}:
scenarios:
rails32:
from: ruby:2.2
rails51:
from: ruby:2.4
The bundle
volume will hold the installed dependencies for each scenario, so
that you don't have to reinstall them from scratch every time you run anything.
Once you run roadshow generate
to create the scenarios
directory, you can
add files called scenarios/rails32.gemfile
and scenarios/rails51.gemfile
containing the gem dependencies for each scenario. For example,
scenarios/rails51.gemfile
could look like this:
source "http://rubygems.org"
gem "rails", "5.1.0"
gem "sqlite3"
gemspec :path => "../"
Now you can just use roadshow run
to run your tests across both versions.
Example: Testing code with database dependencies
This is similar to the last example, but uses supporting containers -- a MySQL instance in one scenario and a PostgreSQL instance in the other.
The scenarios.yml
is more involved, using the services
key to specify extra
containers and the links
key to connect them to the main service:
project: databases
shared:
from: ruby:2.4
cmd: "(bundle check || bundle install) && bundle exec ruby test.rb"
service:
volumes:
- bundle_{{scenario_name}}:/usr/local/bundle
environment:
BUNDLE_GEMFILE: scenarios/{{scenario_name}}.gemfile
RAILS_ENV: development
volumes:
bundle_{{scenario_name}}:
data_{{scenario_name}}:
scenarios:
mysql:
service:
links:
- mysql:database_host
environment:
DATABASE_ADAPTER: mysql2
DATABASE_PORT: 3306
DATABASE_USER: root
services:
mysql:
image: mysql:8.0
volumes:
- "data_mysql:/var/lib/mysql"
environment:
MYSQL_ROOT_PASSWORD: database_password
postgres:
service:
links:
- postgres:database_host
environment:
DATABASE_ADAPTER: postgresql
DATABASE_PORT: 5432
DATABASE_USER: postgres
services:
postgres:
image: postgres:9.6
volumes:
- "data_postgres:/var/lib/postgresql/data"
environment:
POSTGRES_PASSWORD: database_password
You can see the rest of the code here.
License and Acknowledgements
Roadshow is available under the terms of the MIT license (see the LICENSE file for details). It was inspired by thoughtbot's excellent Appraisal tool for Ruby.