Permalink
Browse files

release initial 1.0.0.rc1

  • Loading branch information...
sethvargo committed Dec 11, 2012
1 parent b27cb0a commit e659a9413bce6e1167b109b62db8f70737aa6db4
View
@@ -15,3 +15,4 @@ spec/reports
test/tmp
test/version_tmp
tmp
+sandbox/
View
@@ -1,6 +1,12 @@
Strainer CHANGELOG
==================
+v1.0.0
+------
+- Moved entirely to Berkshelf integration
+- Moved entirely to thor
+- **Breaking** - new command `strainer`, old command is deprecated
+
v0.2.1
------
- Support a wider range of chef versions
View
@@ -0,0 +1,8 @@
+Contributing
+============
+
+Needs Your Help
+---------------
+This is a list of features or problem *you* can help solve! Fork and submit a pull request to make Strain even better!
+
+- **Threading** - Run each cookbook's tests (or each cookbook tests test) in a separate thread
View
@@ -1,4 +1,4 @@
-Copyright 2012 CustomInk
+Copyright 2012 Seth Vargo, CustomInk, LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
View
@@ -4,67 +4,47 @@ Strainer is a gem for isolating and testing individual chef cookbooks. It allows
Usage
-----
-Strainer is a command line tool. The first thing you should do is add the following entry into your `.gitignore` and `chefignore` files:
+First, create a `Strainerfile`. Cookbook-level Strainerfiles will take precedence over root-level ones. For simplicity, let's just create one in the root of our chef repository:
- .colander
-
-The `.colander` directory is where strainer puts all your temporary files for testing. You should not commit it to source control, nor should you upload it if sharing this cookbook with the community.
-
-Next, create a `Colanderfile`. Cookbook-level Colanderfiles will take precedence over root-level ones. For simplicity, let's just create one in the root of our chef repository:
-
- # Colanderfile
+ # Strainerfile
knife test: bundle exec knife cookbook test $COOKBOOK
foodcritic: bundle exec foodcritic -f any cookbooks/$COOKBOOK
-`Colanderfile` exposes two variables:
+`Strainerfile` exposes two variables:
- `$COOKBOOK` - the current running cookbook
- `$SANDBOX` - the sandbox path
Just like foreman, the labels don't actually matter - they are only used in formatting the output.
-That `Colanderfile` will run [foodcritic](https://github.com/acrmp/foodcritic) and knife test. I recommend this as the bare minimum for a cookbook test.
+That `Strainerfile` will run [foodcritic](https://github.com/acrmp/foodcritic) and knife test. I recommend this as the bare minimum for a cookbook test.
-`Colanderfile`s commands are run in the context to the sandbox `.colander` directory. The sandbox is essentially a clone of your working directory. This can be a bit confusing. `knife cookbook test` requires that you run your command against the "root" directory, yet foodcrtitic and chefspec require you run inside an actual cookbook. Here's a quick example to clear up some confusion:
+`Strainerfile`s commands are run in the context to the sandbox directory. The sandbox is essentially a clone of your working directory. This can be a bit confusing. `knife cookbook test` requires that you run your command against the "root" directory, yet foodcrtitic and chefspec require you run inside an actual cookbook. Here's a quick example to clear up some confusion:
- # Colanderfile
+ # Strainerfile
knife test: bundle exec knife cookbook test $COOKBOOK
foodcritic: bundle exec foodcritic -f any $SANDBOX/$COOKBOOK
chefspec: bundle exec rspec $SANDBOX/$COOKBOOK
To strain, simply run the `strain` command and pass in the cookbook(s) to strain:
- $ bundle exec strain phantomjs tmux
-
-This will first detect the cookbook dependencies, copy the cookbook and all dependencies into a sandbox. It will execute the contents of the `Colanderfile` on each cookbook.
-
-Using Berkshelf
----------------
-[Berkshelf](http://berkshelf.com/) is a tool for managing multiple cookbooks. It works very similar to how a `Gemfile` works with Rubygems.
-
-You'll need to tell Berkshelf to install the cookbooks to a folder inside your local repository. This way, strainer can actually find them.
-
-For example, this will install your cookbooks into the `berks-cookbooks` directory:
-
- $ bundle exec berks install --path berks-cookbooks
-
-Finally, make sure that this path is **first** in your `.chef/knife.rb` file:
+ $ bundle exec strainer test phantomjs tmux
-```ruby
-# .chef/knife.rb
-current_dir = File.dirname(__FILE__)
-cookbook_path ["#{current_dir}/../berks-cookbooks", "#{current_dir}/../cookbooks"]
-```
+This will first detect the cookbook dependencies, copy the cookbook and all dependencies into a sandbox. It will execute the contents of the `Strainerfile` on each cookbook.
-Or pass it as an argument to the `strain` command:
+Berkshelf
+---------
+[Berkshelf](http://berkshelf.com/) is a tool for managing multiple cookbooks. It works very similar to how a `Gemfile` works with Rubygems. If you're already using Berkshelf, Strainer will work out of the box. If you're not using Berkshelf, Strainer will work out of the box.
- $ bundle exec strain phantomjs --cookbooks-path berks-cookbooks
+Librarian Chef
+--------------
+Strainer does not support librarian-chef, and I have no plans to implement this feature. PRs are welcome, but Strainer is closely tied to Berkshelf, intentionally.
Failing Quickly
---------------
As of `v0.0.4`, there's an option for `--fail-fast` that will fail immediately when any strain command returns a non-zero exit code:
- $ bundle exec strain phantomjs --fail-fast
+ $ bundle exec strainer test phantomjs --fail-fast
This can save time, especially when running tests locally. This is *not* recommended on continuous integration.
@@ -74,7 +54,7 @@ I always advocate using both [Etsy Foodcritic Rules](https://github.com/etsy/foo
Strainer runs everything in an isolated sandbox, inside your Chef Repo. When including additional foodcritic rules, you need to do something like this:
- # Colanderfile
+ # Strainerfile
foodcritic: bundle exec foodcritic -I foodcritic/* -f any $SANDBOX/$COOKBOOK
Needs Your Help
View
@@ -1,8 +1,9 @@
#!/usr/bin/env ruby
-
$:.unshift File.expand_path('../lib', __FILE__)
-
require 'strainer'
-require 'strainer/cli'
-Strainer::CLI.run(*ARGV)
+# This executable is deprecated
+#
+# @deprecated use `bundle exec strainer test` instead!
+Strainer.ui.deprecated 'The `strain` command is deprecated. Use `strainer test $COOKBOOKS` instead!'
+system "strainer test #{ARGV.join(' ')}"
View
@@ -0,0 +1,11 @@
+#!/usr/bin/env ruby
+$:.unshift File.expand_path('../lib', __FILE__)
+require 'strainer'
+
+begin
+ Strainer::Cli.start
+rescue Strainer::Error::Base => e
+ Strainer.ui.error "#{e.class} - #{e}"
+ Strainer.ui.error " #{e.backtrace.join("\n ")}" if ENV['DEBUG']
+ exit e.status_code
+end
@@ -0,0 +1 @@
+require 'berkshelf/extensions/cached_cookbook'
@@ -0,0 +1,20 @@
+module Berkshelf
+ # Extensions on Berkshelf::CachedCookbook
+ #
+ # @author Seth Vargo <sethvargo@gmail.com>
+ class CachedCookbook
+ # @return [Pathname]
+ # the original location of this cookbook
+ attr_reader :original_path
+
+ # Allow overriding the path, but store the old path in another
+ # instance variable.
+ #
+ # @param [Pathname] location
+ # the new location for this cookbook
+ def path=(location)
+ @original_path = @path.dup
+ @path = location
+ end
+ end
+end
View
@@ -1,9 +1,55 @@
-require 'strainer/color'
-require 'strainer/runner'
-require 'strainer/sandbox'
+require 'berkshelf'
+require 'berkshelf/extensions'
+require 'pathname'
+require 'thor'
+
+require 'strainer/errors'
module Strainer
- def self.root
- @@root ||= File.expand_path('../../', __FILE__)
+ autoload :Cli, 'strainer/cli'
+ autoload :Command, 'strainer/command'
+ autoload :Runner, 'strainer/runner'
+ autoload :Sandbox, 'strainer/sandbox'
+ autoload :Strainerfile, 'strainer/strainerfile'
+ autoload :UI, 'strainer/ui'
+ autoload :Version, 'strainer/version'
+
+ class << self
+ # The root of the application
+ #
+ # @return [Pathname]
+ # the path to the root of Strainer
+ def root
+ @root ||= Pathname.new(File.expand_path('../../', __FILE__))
+ end
+
+ # The UI instance
+ #
+ # @return [Strainer::UI]
+ # an instance of the strainer UI
+ def ui
+ @ui ||= Strainer::UI.new
+ end
+
+ # Helper method to access a constant defined in Strainer::Sandbox that
+ # specifies the location of the sandbox
+ #
+ # @return [Pathname]
+ # the path to the sandbox
+ def sandbox_path
+ Strainer::Sandbox::SANDBOX
+ end
+
+ # Helper method to access a constant defined in Strainer::Strainerfile that
+ # specifies the filename for Strainer
+ #
+ # @return [String]
+ # the filename for Strainerfile
+ def strainerfile_name
+ Strainer::Strainerfile::FILENAME
+ end
end
end
+
+# Sync STDOUT to get "real-time" output
+STDOUT.sync = true
View
@@ -1,52 +1,39 @@
-require 'optparse'
+require 'strainer'
module Strainer
- class CLI
- def self.run(*args)
- parse_options(*args)
-
- if @cookbooks.empty?
- puts Color.red { 'ERROR: You did not specify any cookbooks!' }
- else
- @sandbox = Strainer::Sandbox.new(@cookbooks, @options)
- @runner = Strainer::Runner.new(@sandbox, @options)
- end
+ # Use our own custom shell
+ Thor::Base.shell = Strainer::UI
+
+ # Cli runner for Strainer
+ #
+ # @author Seth Vargo <sethvargo@gmail.com>
+ class Cli < Thor
+ # global options
+ map ['-v', '--version'] => :version
+ class_option :cookbooks_path, :type => :string, :aliases => '-p', :desc => 'The path to the cookbook store', :banner => 'PATH'
+ class_option :config, :type => :string, :aliases => '-c', :desc => 'The path to the knife.rb config'
+
+ # strainer test *COOKBOOKS
+ method_option :except, :type => :array, :aliases => '-e', :desc => 'Strainerfile labels to ignore'
+ method_option :only, :type => :array, :aliases => '-o', :desc => 'Strainerfile labels to include'
+ method_option :fail_fast, :type => :boolean, :aliases => '-x', :desc => 'Stop termination immediately if a test fails', :banner => '', :default => false
+ desc 'test [COOKBOOKS]', 'Run tests against the given cookbooks'
+ def test(*cookbooks)
+ Strainer::Runner.new(cookbooks, options)
end
- private
- def self.parse_options(*args)
- @options = {}
-
- parser = OptionParser.new do |options|
- # remove OptionParsers Officious['version'] to avoid conflicts
- options.base.long.delete('version')
-
- options.on nil, '--fail-fast', 'Fail fast' do |ff|
- @options[:fail_fast] = ff
- end
-
- options.on '-p PATH', '--cookbooks-path PATH', 'Path to the cookbooks' do |cp|
- @options[:cookbooks_path] = cp
- end
-
- options.on '-h', '--help', 'Display this help screen' do
- puts options
- exit 0
- end
-
- options.on '-v', '--version', 'Display the current version' do
- require 'strainer/version'
- puts Strainer::VERSION
- exit 0
- end
- end
+ # strainer info
+ desc 'info', 'Display version and copyright information'
+ def info
+ Strainer.ui.info "Strainer (#{Strainer::VERSION})"
+ Strainer.ui.info "\n"
+ Strainer.ui.info File.read Strainer.root.join('LICENSE')
+ end
- # Get the cookbook names. The options that aren't read by optparser are assummed
- # to be cookbooks in this case.
- @cookbooks = []
- parser.order!(args) do |noopt|
- @cookbooks << noopt
- end
+ # strainer -v
+ desc 'version', 'Display the version information', hide: true
+ def version
+ Strainer.ui.info Strainer::VERSION
end
end
end
View
@@ -1,7 +0,0 @@
-require 'term/ansicolor'
-
-module Strainer
- class Color
- extend Term::ANSIColor
- end
-end
Oops, something went wrong.

0 comments on commit e659a94

Please sign in to comment.