= Cartage by Kinetic Cafe
code :: https://github.com/KineticCafe/cartage/
issues :: https://github.com/KineticCafe/cartage/issues
docs :: http://www.rubydoc.info/github/KineticCafe/cartage/master
continuous integration :: {}[https://travis-ci.org/KineticCafe/cartage]
== 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:
1. 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.
2. The Git hashref is written to the work area (as +release_hashref+) and to
the package staging area.
3. 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.)
4. Bundler is fetched into the work area, and the bundle is installed into the
work area’s +vendor/bundle+ without the +development+ and +test+
environments. If a bundle cache is kept (by default, one is), the resulting
+vendor/bundle+ will be put into a bundle cache so that future bundle
installs are faster.
5. 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+ and +bzip2+ using tar cfj.
* Cartage only understands +git+, which is used for creating
release_hashrefs, +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}[http://cmdparse.gettalong.org/], 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}[https://github.com/crohr/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 are +cartage-s3+ (upload the resulting
package to S3), +cartage-remote+ (build on a remote machine), and
+cartage-rack+ (provide a Rack application to read the +release_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}[http://semver.org/] 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