Cartage by Kinetic Cafe
- code
- issues
- docs
- continuous integration
-
<img src=“https://travis-ci.org/KineticCafe/cartage.svg?branch=master” alt=“Build Status” />
Description
Cartage provides a plug-in based tool to reliably create a package for a Bundler-based Ruby application that can be used in deployment with a configuration tool like Ansible, Chef, Puppet, or Salt. The package is created with its dependencies bundled in vendor/bundle
, so it can be deployed in environments with strict access control rules and without requiring development tool access.
Cartage has learned its tricks from Heroku, Capistrano, and Hoe. From Hoe, it learned to keep a manifest to control what is packaged (as well as its plug-in system). From Heroku, it learned to keep a simple ignore file. From Capistrano, it learned to mark the Git hashref as a file in its built package, and to timestamp the packages.
Cartage follows a relatively simple set of steps when creating a package:
-
Copy the application files to the work area. The application’s files are specified in
Manifest.txt
and filtered against the exclusion list (.cartignore
). If there is no.cartignore
, try to use.slugignore
. If there is no.slugignore
, Cartage will use a sensible default exclusion list. To override the use of this exclusion list, an empty.cartignore
file must be present. -
The Git hashref is written to the work area (as
release_hashref
) and to the package staging area. -
Files that have been modified are restored to pristine condition in the work area. The source files are not touched. (This ensures that
config/database.yml
, for example, will not be the version used by a continuous integration system.) -
Bundler is fetched into the work area, and the bundle is installed into the work area’s
vendor/bundle
without thedevelopment
andtest
environments. If a bundle cache is kept (by default, one is), the resultingvendor/bundle
will be put into a bundle cache so that future bundle installs are faster. -
A timestamped tarball is created from the contents of the work area. It can then be copied to a more permanent or accessible location.
Cartage is extremely opinionated about its tools and environment:
-
The packages are created with
tar
andbzip2
usingtar cfj
. -
Cartage only understands
git
, which is used for creatingrelease_hashref
s,Manifest.txt
creation and comparison, and even default application name detection (from the name of the origin remote).
Synopsis
# Build a package from the current machine, using the Manifest.txt.
cartage pack
# Create or update a Manifest.txt from the current repository.
cartage manifest
# Check the current Manifest against the files that should be there.
cartage check
# Create a .cartignore file for use.
cartage install-ignore
# Overwrite the current .cartignore with the default.
cartage install-ignore --force
# Merge the current .cartignore with the default. Merging automatically
# removes any comments.
cartage install-ignore --merge
Install
Add cartage to your Gemfile:
gem 'cartage', '~> 1.0', groups: [ :development, :test ]
Or manually install:
% gem install cartage
Cartage Plug-Ins
Cartage is extensible by plug-ins. This version of the plug-in system provides one integration point, subcommands. Cartage is implemented with cmdparse, and plug-ins implement a class that performs the work (nested under the Cartage namespace) and one or more commands that execute on the work. Plug-in files are discovered immediately relative to the cartage
directory, and are registered on inheritance from Cartage::Plugin. This plug-in would be found in 'cartage/info.rb'
. Each plug-in will get a lazy-instantiation constructor added to Cartage itself that provides a reference to the owning Cartage instance.
Below is an example of a Cartage plug-in to provide a cartage info
command. This command isn’t very useful, since most values used in Cartage packaging are lazily resolved, but it demonstrates how simple a plug-in can be.
require 'cartage/plugin'
require 'cartage/command'
# An instance of this will be created lazily by calling Cartage#info.
class Cartage::Info < Cartage::Plugin
# It does not matter what this method is. It’s just a public instance
# method used by the command.
def run
@cartage.instance_variables.sort.each { |ivar|
puts "#{ivar}: #{@cartage.instance_variable_get(ivar)}"
}
end
# This will create a CmdParse command that responds to 'info' and takes
# no subcommands. If the plug-in provides a number of features, it is
# recommended that subcommands be used.
class InfoCommand < Cartage::Command
# The Cartage::Command objects are initialized with the cartage
# instance. Set the command name with the string here.
def initialize(cartage)
super(cartage, 'info', takes_commands: false)
short_desc('Shows configuration information about Cartage.')
end
# This is run by Cartage::Command#execute to make the command happen.
# An exception should be thrown on failure, as return values do not
# affect the status code of the application.
def perform
@cartage.info.run
end
end
# This returns the command(s) created by this plug-in.
def self.commands
[ Cartage::Info::InfoCommand ]
end
end
For a more comprehensive example, see the implementation of Manifest (lib/cartage/manifest.rb
) and its commands (lib/cartage/manifest/commands.rb
).
Future releases of Cartage will offer additional ways to extend Cartage.
Alternate Projects
The closest project to Cartage is pkgr. Pkgr will create a distribution package for Ubuntu (14.04 and 12.02, Debian 7), CentOS 6, SLES 12, and Fedora 20.
Both Cartage and Pkgr provide isolated builds with all in-application dependencies included.
Pkgr offers several advantages over Cartage:
-
It supports more languages than just Ruby (suport for Go and Node.js are confirmed and more are in progress). Cartage will probably have more language support in the future, but it is not an immediate priority. For Ruby and Node.js, the interpreters are included locally to the application.
-
It creates a distribution package, meaning that you can just use
apt-get
to install or upgrade your package. -
It reuses Heroku buildpacks (this requires that your application behave a bit more like a Heroku application, which may be a bit more
Cartage offers advantages over Pkgr:
-
Cartage offers a plug-in based extension system. While the plug-in system is currently limited to adding new
cartage
commands, this is not its only possible use. Existing plug-ins arecartage-s3
(upload the resulting package to S3),cartage-remote
(build on a remote machine), andcartage-rack
(provide a Rack application to read therelease_hashref
over an API call). -
Cartage makes it easier to integrate into a workflow translated from Capistrano, as it essentially replaces the source control checkout stage. This process makes it really easy to integrate into an Ansible playbook (as we have done at Kinetic Cafe).
Cartage Semantic Versioning
Cartage uses a Semantic Versioning scheme with one significant change:
-
When PATCH is zero (
0
), it will be omitted from version references.
Additionally, the major version will generally be reserved for plug-in infrastructure changes.
:include: Contributing.rdoc
:include: Licence.rdoc