Compare commits

..

No commits in common. "master" and "develop" have entirely different histories.

56 changed files with 200 additions and 1687 deletions

1
.gitignore vendored
View File

@ -2,4 +2,3 @@
.bundle
Gemfile.lock
pkg/*
.DS_Store

View File

@ -5,8 +5,6 @@ gemspec
gem 'guard'
gem 'guard-rspec'
gem 'guard-cucumber'
# see, *this* is why you need penchant
#gem 'cuke-pack', :path => '../cuke-pack'
gem 'cuke-pack', :git => 'git://github.com/johnbintz/cuke-pack.git'
gem 'mocha'
gem 'fakefs'
gem 'rspec', '~> 2.6.0'

View File

@ -1,21 +1,9 @@
# A sample Guardfile
# More info at https://github.com/guard/guard#readme
group :rspec do
guard 'rspec', :cli => '-c', :version => 2 do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { "spec" }
end
guard 'rspec', :cli => '-c', :version => 2 do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { "spec" }
end
# added by cuke-pack
group :wip do
guard 'cucumber', :env => :cucumber, :cli => '-p wip' do
watch(%r{^features/.+.feature$})
watch(%r{^(app|lib).*}) { 'features' }
watch(%r{^features/support/.+$}) { 'features' }
watch(%r{^features/step_definitions/(.+).rb$}) { 'features' }
end
end

241
README.md
View File

@ -2,11 +2,9 @@
I like to do these things in all my projects:
* Have all my tests run before committing. I don't like buying ice cream for the team on test failures, and setting up internal
CI for smaller projects is a pain.
* If I'm developing gems alongside this project, I use a `Gemfile.penchant` to get around the "one gem, one source" issue in
* Have all my tests run before committing. I don't like buying ice cream for the team on test failures.
* If I'm developing gems alongside this project, I use a `Gemfile.erb` to get around the "one gem, one source" issue in
current versions of Bundler.
* I can also factor out and simplify a lot of my Gemfile settings.
* If I'm moving to different machines or (heaven forbid!) having other developers work on the project, I want to make
getting all those local gems as easy as possible.
@ -16,238 +14,39 @@ This gem makes that easier!
Installs a bunch of scripts into the `scripts` directory of your project:
* `gemfile` which switches between `Gemfile.penchant` environments
* `gemfile` which switches between `Gemfile.erb` environments
* `install-git-hooks` which will do just what it says
* `hooks`, several git hooks that the prior script symlinks into .git/hooks for you
* `hooks/pre-commit`, one of the hooks the prior script installs
* `initialize-environment`, which bootstraps your local environment so you can get up and running
## Gemfile.penchant?!
Yeah, it's a `Gemfile` with some extras:
``` ruby
# Gemfile.penchant
source :rubygems
# ensure git hooks are installed when a gemfile is processed, see below
ensure_git_hooks!
# need the bundler UTF-8 fix? ask for it by name!
bundler_encoding_fix!
# deploying to heroku and want 1.9.3 goodness?
ruby '1.9.3'
gem 'rails', '3.2.3'
# expands to:
#
# gem 'rake'
# gem 'nokogiri'
# gem 'rack-rewrite'
gems 'rake', 'nokogiri', 'rack-rewrite'
# define custom gem properties that get expanded to ones bundler understands
property :github, :git => 'git://github.com/$1/%s.git'
# values to the key are [ value ].flatten-ed and the $s are replaced on the fly,
# with $1 being the first parameter given
# set up defaults for all gems in a particular environment
defaults_for env(:local), :path => '../%s' # the %s is the name of the gem
no_deployment do
group :development, :test do
gem 'rspec', '~> 2.6.0'
dev_gems = %w{flowerbox guard-flowerbox}
# set up defaults for certain gems that are probably being used in envs
defaults_for dev_gems, :require => nil
env :local do
# expands to:
#
# gem 'flowerbox', :path => '../flowerbox', :require => nil
# gem 'guard-flowerbox', :path => '../guard-flowerbox', :require => nil
gems dev_gems
end
env :remote do
# expands to:
#
# gem 'flowerbox', :git => 'git://github.com/johnbintz/flowerbox.git', :require => nil
# gem 'guard-flowerbox', :git => 'git://github.com/johnbintz/guard-flowerbox.git', :require => nil
gems dev_gems, :github => 'johnbintz'
end
# an even shorter way to specify environments!
# in remote env, expands to:
# gem 'bullseye', :git => 'git://github.com/johnbintz/bullseye.git'
# in local env, expands to:
# gem 'bullseye', :path => '../bullseye'
env :remote, :opposite => :local do
gem 'bullseye', :github => 'johnbintz'
end
# only expanded on Mac OS X
os :darwin do
gem 'rb-fsevent'
end
# only expanded on Linux
os :linux do
gems 'rb-inotify', 'ffi'
end
end
end
```
Use `script/gemfile local` to get at the local ones, and `script/gemfile remote` to get at the remote ones.
It then runs `bundle install`.
You can also run `penchant gemfile ENV`. Just straight `penchant gemfile` will rebuild the `Gemfile` from
`Gemfile.penchant` for whatever environment the `Gemfile` is currently using.
If you have an existing project, `penchant convert` will convert the `Gemfile` into a `Gemfile.penchant`
and add some bonuses, like defining that anything in `env :local` blocks automatically reference `..`,
ensuring that hooks are always installed when `penchant gemfile` is executed, and adding the `:github` gem property
that lets you pass in the username of the repo to reference that repo:
`gem 'penchant', :github => 'johnbintz'`.
### Stupid-simple local/remote setup
Use `opposites :local, :remote` and environment settings for local/remote gems will be set accordingly depending
on environment:
``` ruby
defaults_for env(:local), :path => '../%s'
opposites :local, :remote
env :remote do
gem 'my-gem', :git => 'git://github.com/johnbintz/my-gem.git'
end
```
In `remote`, the Git repo version is used. In `local`, the path is used. Only one gem definition needed!
### Deployment mode
Use `no_deployment` blocks to indicate gems that shouldn't even appear in `Gemfiles` destined for
remote servers. *Very* helpful when you have OS-specific gems and are developing on one platform
and deploying on another, or if you don't want to deal with the dependencies for your testing
frameworks:
``` ruby
gem 'rails'
no_deployment do
os :darwin do
gems 'growl_notify', 'growl', 'rb-fsevent'
end
os :linux do
gem 'libnotify', :require => nil
end
group :test do
# ... all your testing libraries you won't need on the deployed end ...
end
end
```
Run `penchant gemfile ENV --deployment` to get this behavior. This is run by default when the
pre-commit git hook runs, but only after the default Rake task passes.
If you just want any locally installed gems, add the `--local` switch. Great if rubygems.org is down!
#### Won't this change the project dependencies?!
Probably not. You probably have the "main" gems in your project locked to a version of Rails or
Sinatra or something else, and all of the other gems for authentication, queue processing, etc. are
dependent on that framework. Ripping out your testing framework and deployment helpers really
shouldn't be changing the main versions of your application gems. It WORKSFORME and YMMV.
### Getting local gems all set up
`penchant bootstrap` will go through and find all git repo references in your `Gemfile.penchant` and
will download them to the specified directory (by default, `..`). This means blocks like this
will work as expected when you `penchant bootstrap` and then `penchant gemfile local`:
``` ruby
env :local do
gem 'my-gem', :path => '../%s'
end
env :remote do
gem 'my-gem', :git => 'git://github.com/johnbintz/%s.git'
end
```
Note that this just does a quick `git clone`, so if your project is already in there in a different state,
nothing "happens" except that git fails.
## initialize-environment
Get new developers up to speed fast! `script/initialize-environment` does the following when run:
It will also try to run `rake bootstrap`, so add a `:bootstrap` task for things that should happen when you start going
(make databases, other stuff, etc, whatever).
* Check out any remote repos found in `Gemfile.penchant` to the same directory where your current project lives.
That way, you can have your `Gemfile.penchant` set up as above and everything works cleanly.
* Runs `script/gemfile remote` to set your project to using remote repositories.
* Runs `rake bootstrap` for the project if it exists.
## Gemfile.erb?!
### After-`gemfile` hooks?
Yeah, it's a `Gemfile` with ERB in it:
Drop a file called `.penchant` in your project directory. It'll get executed every time you switch environments using
Penchant. I use it to tell my Hydra clients to sync and update their Gemfiles, too:
``` ruby
# rake knows if you need "bundle exec" or not.
rake "hydra:sync hydra:remote:bundle"
``` erb
<% if env == "local" %>
gem 'guard', :path => '../guard'
<% else %>
gem 'guard', :git => 'git://github.com/johnbintz/guard.git'
<% end %>
```
### What environment are you currently using in that Gemfile?
`head -n 1` that puppy, or `penchant gemfile-env`.
Use `script/gemfile local` to get at the local ones, and `script/gemfile remote` (or anything, really) to get at the remote ones.
It then runs `bundle install`.
## git hook?!
It runs `penchant gemfile remote` then runs `bundle exec rake`. Make sure your default Rake task for the project runs your
tests and performs any other magic necessary before each commit. Your re-environmented Gemfile and Gemfile.lock will be added
to your commit if they've changed.
### Ensuring git hooks get installed
I find that when I pull down new projects I never remember to install the git hooks, which involves an awkward running
of `bundle exec rake` *after* I've already committed code. Since we have computers now, and they can be told to do things,
you can add `ensure_git_hooks!` anywhere in your `Gemfile.penchant` to make sure the git hooks are symlinked to the ones
in the `script/hooks` directory with every processing of `Gemfile.penchant`.
### Performing pre-`bundle exec rake` tasks.
Example: I use a style of Cucumber testing where I co-opt the `@wip` tag and then tell Guard to only run scenarios with `@wip` tags.
I don't want `@wip` tasks to be committed to the repo, since committing a half-completed scenario seems silly.
So I use `bundle exec rake preflight_check` to check all feature files for `@wip` tasks, and to fail if I hit one. Yes, Cucumber
already does this, but in order to get to `bundle exec rake`, I need to go through two `Gemfile` creations, one for `remote --deployment`
and one for `remote` to make sure my tests work on remote gems only.
If `bundle exec rake -T preflight_check` returns a task, that task will be run before all the `Gemfile` switcheroo. *Don't use it
as a place to run your tests!*
### Skipping all that Rake falderal?
Do it Travis CI style: stick `[ci skip]` in your commit message. That's why the meat of the git hooks resides in
`commit-msg` and not `pre-commit`: you need the commit message before you can determine if the tests should be run
based on the commit message. Weird, I know.
It runs `script/gemfile remote` then runs `bundle exec rake`. Make sure your default Rake task for the project runs your
tests and performs any other magic necessary before each commit.
## How?!
* No RVM? `gem install penchant`
* RVM? `rvm gemset use global && gem install penchant && rvm gemset use default`
* `gem install penchant`
* `cd` to your project directory
And then one of the following:
* `penchant install` for a new project (`--dir=WHEREVER` will install the scripts to a directory other than `$PWD/scripts`)
* `penchant update` to update the installation (`--dir=WHEVEVER` works here, too)
* `penchant convert` for an existing project (`--dir=WHEVEVER` works here, too)
* `penchant install` (can do `--dir=WHEREVER`, too)

View File

@ -1,16 +1,2 @@
require 'bundler'
Bundler::GemHelper.install_tasks
begin
require 'cucumber'
require 'cucumber/rake/task'
Cucumber::Rake::Task.new(:cucumber) do |t|
t.cucumber_opts = "features --format pretty"
end
rescue LoadError
"#$! - no cucumber"
end
task :default => [ :cucumber ]

View File

@ -3,157 +3,28 @@
require 'rubygems'
require 'thor'
require 'penchant'
require 'fileutils'
class PenchantCLI < Thor
include Thor::Actions
source_root File.expand_path('../..', __FILE__)
SCRIPT_DIR = 'script'
CLONE_DIR = '..'
desc "install", "Copy the common scripts to the project"
method_options :dir => SCRIPT_DIR
method_options :dir => 'script'
def install
directory 'template/script', options[:dir]
Dir[File.join(options[:dir], '**/*')].each { |file| File.chmod(0755, file) }
if File.directory?('.git')
Penchant::Hooks.install!
else
puts "No git repository detected here. Skipping git hook installation..."
end
if !File.file?('Gemfile') && !File.file?('Gemfile.penchant')
FileUtils.touch('Gemfile.penchant')
prepend_to_file 'Gemfile.penchant', <<-RB
source :rubygems
RB
install_gemfile_penchant
end
end
desc "update", "Update the installed scripts"
method_options :dir => SCRIPT_DIR
def update
install
end
desc "convert", "Make an existing project Penchant-isized"
method_options :dir => SCRIPT_DIR
def convert
install
FileUtils.mv 'Gemfile', 'Gemfile.penchant'
install_gemfile_penchant
end
method_options :deployment => false
method_options :switch_back => false
method_options :no_auto_update => false
method_options :local => false
desc "gemfile ENV", "Switch the gemfile environment, or rebuild the current environment if not given"
def gemfile(env = get_current_env)
check_git_hooks!
if env
if options[:switch_back]
puts "[penchant] Switching back, fallback: #{env}..."
Penchant::Gemfile.switch_back!(env)
else
puts "[penchant] Rebunding for #{env} environment#{options[:deployment] ? ", deployment mode" : ''}..."
Penchant::Gemfile.do_full_env_switch!(env, options[:deployment])
end
end
gemfile = Penchant::Gemfile.new
if !gemfile.has_gemfile?
puts "No Gemfile or Gemfile.penchant, exiting."
exit 1
end
command = %{bundle}
command << " --local" if options[:local]
system command
# it's asking for bundle update, we know what we're doing
if $?.exitstatus == 6 and !options[:no_auto_update]
command = %{bundle update}
command << " --local" if options[:local]
system command
end
desc "gemfile ENV", "Switch the gemfile environment"
def gemfile(env)
Penchant::Gemfile.do_full_env_switch!(env)
end
desc "gemfile-env", "Get the gemfile environment"
def gemfile_env
puts get_current_env
gemfile = Penchant::Gemfile.new
puts gemfile.environment
end
desc "bootstrap [DIR = #{CLONE_DIR}]", "Download all referred-to git repos to the specified directory"
def bootstrap(dir = CLONE_DIR)
Penchant::Gemfile.defined_git_repos.each do |repo|
puts "Cloning #{repo} to #{dir}"
repo.clone_to(dir)
end
end
def method_missing(method, *args)
if Penchant::Gemfile.available_environments.include?(method)
gemfile(method, *args)
else
super(method, *args)
end
end
no_tasks do
def get_current_env
gemfile = Penchant::Gemfile.new
out = [ gemfile.environment ]
out << "deployment" if gemfile.deployment?
out.join(' ')
end
def check_git_hooks!
if !Penchant::Hooks.installed?
puts "[penchant] git hooks not installed. Run script/install-git-hooks."
puts
end
end
def install_gemfile_penchant
prepend_to_file 'Gemfile.penchant', <<-RB
# ensure git hooks are always installed
ensure_git_hooks!
# everything in the :local env is assumed to be a sibling directory of this one
defaults_for env(:local), :path => '../%s'
# reference a github repository with gem 'my-gem', :github => 'username'
# also supports modern bundler user/repo syntax
property(:github) { |name|
parts = name.split('/')
url = case parts.length
when 1
"git://github.com/\#{name}/%s.git"
when 2
"git://github.com/\#{parts.first}/\#{parts.last}.git"
end
{ :git => url }
}
RB
gemfile(:remote)
end
end
default_task :gemfile
end
PenchantCLI.start

View File

@ -1,8 +0,0 @@
<%
std_opts = "-r features --format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} -f Cucumber::StepWriter --out features/step_definitions --strict"
%>
default: <%= std_opts %> features
wip: <%= std_opts %> --tags @wip features
precommit: FAILFAST=true <%= std_opts %> --tags ~@wip:0 features
cleanup: <%= std_opts %> -f Cucumber::CleanupFormatter --out unused.txt features

View File

@ -1,77 +0,0 @@
Feature: CLI
Scenario: Switch back to the original pre-deployment environment
Given I have the file "tmp/Gemfile.penchant" with the content:
"""
gem 'rake'
"""
And I have the file "tmp/Gemfile" with the content:
"""
# generated by penchant, environment: production, deployment mode (was local)
"""
When I run "bin/penchant gemfile other --switch-back" in the "tmp" directory
Then the file "tmp/Gemfile" should have the following content:
"""
# generated by penchant, environment: local
gem "rake"
"""
And the output should include "fallback: other"
Scenario: Try to convert a project, ignoring git hooks
Given I have the file "tmp/Gemfile" with the content:
"""
source :rubygems
"""
When I run "bin/penchant convert" in the "tmp" directory
Then the file "tmp/Gemfile.penchant" should include the following content:
"""
source :rubygems
"""
And the output should include "No git"
Scenario: Run in a project where the git hooks are not set up
Given I have the file "tmp/Gemfile.penchant" with the content:
"""
gem 'rake'
"""
Given I have the file "tmp/script/hooks/pre-commit" with the content:
"""
a penchant hook
"""
When I run "bin/penchant gemfile remote" in the "tmp" directory
Then the output should include "git hooks not installed"
Scenario: Run in a project where there are no git hooks, but there is a git repo
Given I have the file "tmp/Gemfile.penchant" with the content:
"""
gem 'rake'
"""
Given I have the directory "tmp/.git"
When I run "bin/penchant gemfile remote" in the "tmp" directory
Then the output should not include "git hooks not installed"
Scenario: Run in a project where git hooks are set up
Given I have the file "tmp/Gemfile.penchant" with the content:
"""
gem 'rake'
"""
Given I have the file "tmp/script/hooks/pre-commit" with the content:
"""
a penchant hook
"""
Given I have the symlink "tmp/.git/hooks/pre-commit" which points to "tmp/script/hooks/pre-commit"
When I run "bin/penchant gemfile remote" in the "tmp" directory
Then the output should not include "git hooks not installed"
Scenario: Install Penchant into a directory with no Gemfile
Given I have the directory "tmp"
When I run "bin/penchant install" in the "tmp" directory
Then the file "tmp/Gemfile.penchant" should include the following content:
"""
source :rubygems
"""
Then the file "tmp/Gemfile" should include the following content:
"""
source :rubygems
"""
And the output should include "No git"

View File

@ -1,441 +0,0 @@
@fakefs
Feature: Gemfiles
Scenario: Process a pure Ruby gemfile
Given I have the file "Gemfile.penchant" with the content:
"""
source :rubygems
gemspec
group :cats, :dogs do
case environment
when :local
gem 'test', :path => '../test'
when :remote
gem 'test', :git => 'git://github.com/johnbintz/test.git'
end
end
"""
When I rebuild the Gemfile for "local" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: local
source :rubygems
gemspec
group :cats, :dogs do
gem "test", {:path=>"../test"}
end
"""
When I rebuild the Gemfile for "remote" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: remote
source :rubygems
gemspec
group :cats, :dogs do
gem "test", {:git=>"git://github.com/johnbintz/test.git"}
end
"""
Scenario: Use a gemlist
Given I have the file "Gemfile.penchant" with the content:
"""
gems 'one', 'two', 'three', :path => '../%s'
"""
When I rebuild the Gemfile for "local" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: local
gem "one", {:path=>"../one"}
gem "two", {:path=>"../two"}
gem "three", {:path=>"../three"}
"""
Scenario: Use an env block
Given I have the file "Gemfile.penchant" with the content:
"""
env :local do
gems 'one', 'two', 'three', :path => '../%s'
end
"""
When I rebuild the Gemfile for "local" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: local
gem "one", {:path=>"../one"}
gem "two", {:path=>"../two"}
gem "three", {:path=>"../three"}
"""
When I rebuild the Gemfile for "remote" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: remote
"""
Scenario: Skip deployment blocks
Given I have the file "Gemfile.penchant" with the content:
"""
no_deployment do
gem 'one'
end
"""
When I rebuild the Gemfile for "local" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: local
gem "one"
"""
When I rebuild the Gemfile for "local" mode with deployment
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: local, deployment mode (was local)
"""
Scenario: Peel multiple hashes off a gemlist
Given I have the file "Gemfile.penchant" with the content:
"""
gems 'one', { :path => '../%s' }, { :require => nil }
"""
When I rebuild the Gemfile for "local" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: local
gem "one", {:path=>"../one", :require=>nil}
"""
Scenario: Don't add an empty hash
Given I have the file "Gemfile.penchant" with the content:
"""
gems 'one'
"""
When I rebuild the Gemfile for "local" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: local
gem "one"
"""
Scenario: Single gem gets processed like a gems list
Given I have the file "Gemfile.penchant" with the content:
"""
gem 'one', '1.2.3', :path => '../%s'
"""
When I rebuild the Gemfile for "local" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: local
gem "one", "1.2.3", {:path=>"../one"}
"""
@mocha
Scenario: OS-specific blocks
Given I have the file "Gemfile.penchant" with the content:
"""
os :darwin do
gem 'one', :path => '../%s'
end
"""
And I am on the "darwin" platform
When I rebuild the Gemfile for "local" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: local
gem "one", {:path=>"../one"}
"""
Given I am on the "linux" platform
When I rebuild the Gemfile for "local" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: local
"""
Scenario: Get the list of environments defined
Given I have the file "Gemfile.penchant" with the content:
"""
env :cat do
gem 'one', :path => '../%s'
end
env :dog do
gem 'two', :path => '../%s'
end
"""
When I request the list of environments available
Then I should get the following environments:
| cat |
| dog |
Scenario: Get the list of git repos defined
Given I have the file "Gemfile.penchant" with the content:
"""
gem 'one', :path => '../%s'
gem 'two', :git => 'git://github.cats/%s.git'
"""
When I request the list of git repositories
Then I should get the following repositories:
| git://github.cats/two.git |
Scenario: Get the list of git repos defined, regardless of environment
Given I have the file "Gemfile.penchant" with the content:
"""
gem 'one', :path => '../%s'
env :remote do
gem 'two', :git => 'git://github.cats/%s.git'
end
"""
When I request the list of git repositories
Then I should get the following repositories:
| git://github.cats/two.git |
Scenario: Propose defaults for a gem
Given I have the file "Gemfile.penchant" with the content:
"""
defaults_for 'one', :path => '../%s'
gem 'one', '1.2.3'
"""
When I rebuild the Gemfile for "local" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: local
gem "one", "1.2.3", {:path=>"../one"}
"""
Scenario: Propose defaults for an array of gems
Given I have the file "Gemfile.penchant" with the content:
"""
defaults_for ['one'], :path => '../%s'
gem 'one', '1.2.3'
"""
When I rebuild the Gemfile for "local" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: local
gem "one", "1.2.3", {:path=>"../one"}
"""
Scenario: Propose defaults for an environment
Given I have the file "Gemfile.penchant" with the content:
"""
defaults_for env(:local), :path => '../%s'
env :local do
gem 'one', '1.2.3'
end
"""
When I rebuild the Gemfile for "local" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: local
gem "one", "1.2.3", {:path=>"../one"}
"""
Scenario: Create opposite environment gem assumptions to cut down on repetition
Given I have the file "Gemfile.penchant" with the content:
"""
defaults_for env(:local), :path => '../%s'
property(:github) { |name| { :git => "git://github.com/#{name}/%s.git" } }
env :remote, :opposite => :local do
gem 'one', :github => 'johnbintz', :require => nil
end
env :local, :opposite => :remote do
gem 'two', :path => '../%s', :require => nil
end
"""
When I rebuild the Gemfile for "local" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: local
gem "one", {:path=>"../one", :require=>nil}
gem "two", {:path=>"../two", :require=>nil}
"""
When I rebuild the Gemfile for "remote" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: remote
gem "one", {:git=>"git://github.com/johnbintz/one.git", :require=>nil}
gem "two", {:require=>nil}
"""
Scenario: Set the opposite environment in the environment defaults
Given I have the file "Gemfile.penchant" with the content:
"""
defaults_for env(:local), :path => '../%s', :opposite => :remote
defaults_for env(:remote), :opposite => :local
property(:github) { |name| { :git => "git://github.com/#{name}/%s.git" } }
env :remote do
gem 'one', :github => 'johnbintz', :require => nil
end
env :local do
gem 'two', :path => '../%s', :require => nil
end
"""
When I rebuild the Gemfile for "local" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: local
gem "one", {:path=>"../one", :require=>nil}
gem "two", {:path=>"../two", :require=>nil}
"""
When I rebuild the Gemfile for "remote" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: remote
gem "one", {:git=>"git://github.com/johnbintz/one.git", :require=>nil}
gem "two", {:require=>nil}
"""
Scenario: Define a pair of opposites
Given I have the file "Gemfile.penchant" with the content:
"""
defaults_for env(:local), :path => '../%s'
opposites :local, :remote
property(:github) { |name| { :git => "git://github.com/#{name}/%s.git" } }
env :remote do
gem 'one', :github => 'johnbintz', :require => nil
end
env :local do
gem 'two', :path => '../%s', :require => nil
end
"""
When I rebuild the Gemfile for "local" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: local
gem "one", {:path=>"../one", :require=>nil}
gem "two", {:path=>"../two", :require=>nil}
"""
When I rebuild the Gemfile for "remote" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: remote
gem "one", {:git=>"git://github.com/johnbintz/one.git", :require=>nil}
gem "two", {:require=>nil}
"""
Scenario: Define a pair of opposites in the other order
Given I have the file "Gemfile.penchant" with the content:
"""
opposites :local, :remote
defaults_for env(:local), :path => '../%s'
property(:github) { |name| { :git => "git://github.com/#{name}/%s.git" } }
env :remote do
gem 'one', :github => 'johnbintz', :require => nil
end
env :local do
gem 'two', :path => '../%s', :require => nil
end
"""
When I rebuild the Gemfile for "local" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: local
gem "one", {:path=>"../one", :require=>nil}
gem "two", {:path=>"../two", :require=>nil}
"""
When I rebuild the Gemfile for "remote" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: remote
gem "one", {:git=>"git://github.com/johnbintz/one.git", :require=>nil}
gem "two", {:require=>nil}
"""
Scenario: Override defaults for an environment
Given I have the file "Gemfile.penchant" with the content:
"""
defaults_for env(:local), :path => '../%s'
env :local do
gem 'one', '1.2.3', :path => '../cats'
end
"""
When I rebuild the Gemfile for "local" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: local
gem "one", "1.2.3", {:path=>"../cats"}
"""
Scenario: Define special expansions for properties
Given I have the file "Gemfile.penchant" with the content:
"""
defaults_for env(:local), :path => '../%s'
property(:github) { |name| { :git => "git://github.com/#{name}/%s.git" } }
gem 'one', :github => 'john'
"""
When I rebuild the Gemfile for "local" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: local
gem "one", {:git=>"git://github.com/john/one.git"}
"""
Scenario: Define special expansions for properties as a hash
Given I have the file "Gemfile.penchant" with the content:
"""
defaults_for env(:local), :path => '../%s'
property :github, :git => "git://github.com/$1/%s.git"
gem 'one', :github => 'john'
"""
When I rebuild the Gemfile for "local" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: local
gem "one", {:git=>"git://github.com/john/one.git"}
"""
@mocha
Scenario: Force installing of git hooks if the gemfile asks for it
Given I expect git hooks to be installed
Given I have the file "Gemfile.penchant" with the content:
"""
ensure_git_hooks!
gem 'one'
"""
When I rebuild the Gemfile for "local" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: local
gem "one"
"""
Scenario: Pass through the Ruby version
Given I have the file "Gemfile.penchant" with the content:
"""
ruby '1.9.3'
"""
When I rebuild the Gemfile for "local" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: local
ruby "1.9.3"
"""
Scenario: Ask for the Gemfile encoding fix
Given I have the file "Gemfile.penchant" with the content:
"""
bundler_encoding_fix!
"""
When I rebuild the Gemfile for "local" mode
Then the file "Gemfile" should have the following content:
"""
# generated by penchant, environment: local
if RUBY_VERSION =~ /1.9/
Encoding.default_external = Encoding::UTF_8
Encoding.default_internal = Encoding::UTF_8
end
"""

View File

@ -1,3 +0,0 @@
Given /^I am on the "([^"]*)" platform$/ do |os|
Penchant::FileProcessor.any_instance.stubs(:current_os).returns(os.to_sym)
end

View File

@ -1,3 +0,0 @@
Given /^I expect git hooks to be installed$/ do
Penchant::Hooks.expects(:install!)
end

View File

@ -1,3 +0,0 @@
Given /^I have the directory "(.*?)"$/ do |dir|
FileUtils.mkdir_p dir
end

View File

@ -1,5 +0,0 @@
Given /^I have the file "([^"]*)" with the content:$/ do |file, string|
FileUtils.mkdir_p File.dirname(file)
File.open(file, 'wb') { |fh| fh.print string }
end

View File

@ -1,4 +0,0 @@
Given /^I have the symlink "(.*?)" which points to "(.*?)"$/ do |source, target|
FileUtils.mkdir_p(File.dirname(source))
File.symlink(target, source)
end

View File

@ -1,3 +0,0 @@
Then /^I should get the following environments:$/ do |table|
@environments.collect(&:to_s).sort.should == table.raw.flatten.sort
end

View File

@ -1,4 +0,0 @@
Then /^I should get the following repositories:$/ do |table|
@repos.collect(&:to_s).sort.should == table.raw.flatten.sort
end

View File

@ -1,9 +0,0 @@
Then /^the file "(.*?)" should have the following stripped content:$/ do |file, string|
test_lines = string.lines.to_a
File.read(file).lines.collect(&:strip).reject(&:empty?).to_a.each do |line|
raise StandardError.new if test_lines.empty?
line.strip.should == test_lines.shift.strip
end
end

View File

@ -1,3 +0,0 @@
Then /^the file "([^"]*)" should have the following content:$/ do |file, string|
File.read(file).should == string
end

View File

@ -1,3 +0,0 @@
Then /^the file "(.*?)" should include the following content:$/ do |file, string|
File.read(file).should include(string)
end

View File

@ -1,3 +0,0 @@
Then /^the output should include "([^"]*)"$/ do |text|
@output.should include(text)
end

View File

@ -1,3 +0,0 @@
Then /^the output should not include "(.*?)"$/ do |text|
@output.should_not include(text)
end

View File

@ -1,4 +0,0 @@
When /^I rebuild the Gemfile for "(.*?)" mode$/ do |env|
Penchant::Gemfile.do_full_env_switch!(env)
end

View File

@ -1,3 +0,0 @@
When /^I rebuild the Gemfile asking to switch back to the previous state$/ do
Penchant::Gemfile.switch_back!("remote")
end

View File

@ -1,3 +0,0 @@
When /^I rebuild the Gemfile for "([^"]*)" mode with deployment$/ do |env|
Penchant::Gemfile.do_full_env_switch!(env, true)
end

View File

@ -1,3 +0,0 @@
When /^I request the list of environments available$/ do
@environments = Penchant::Gemfile.available_environments
end

View File

@ -1,3 +0,0 @@
When /^I request the list of git repositories$/ do
@repos = Penchant::Gemfile.defined_git_repos
end

View File

@ -1,3 +0,0 @@
When /^I run "([^"]*)" in the "([^"]*)" directory$/ do |command, dir|
@output = %x{bash -c 'opwd=$PWD; cd #{dir} && $opwd/#{command}'}
end

View File

@ -1,20 +0,0 @@
require 'cuke-pack/support/pause'
require 'cuke-pack/support/pending'
Before do
# if you want pending steps to pause before marking the step as pending,
# set @pause_ok to true
@pause_ok = false
end
require 'cuke-pack/support/step_writer'
require 'cuke-pack/support/wait_for'
require 'cuke-pack/support/failfast'
# set the level of flaying on the step definitions
# set it to false to skip flaying
flay_level = 32
require 'cuke-pack/support/flay'

View File

@ -1,27 +0,0 @@
require 'fakefs/safe'
require 'penchant'
require 'mocha'
World(Mocha::API)
Before('@fakefs') do
FakeFS.activate!
end
Before('@mocha') do
mocha_setup
end
After do
FakeFS::FileSystem.clear
FakeFS.deactivate!
begin
mocha_verify
ensure
mocha_teardown
end
FileUtils.rm_rf 'tmp'
end

View File

@ -1,13 +1,3 @@
module Penchant
autoload :Gemfile, 'penchant/gemfile'
autoload :Repo, 'penchant/repo'
autoload :DotPenchant, 'penchant/dot_penchant'
autoload :Hooks, 'penchant/hooks'
autoload :Env, 'penchant/env'
autoload :FileProcessor, 'penchant/file_processor'
autoload :Defaults, 'penchant/defaults'
autoload :CustomProperty, 'penchant/custom_property'
autoload :PropertyStack, 'penchant/property_stack'
autoload :PropertyStackBuilder, 'penchant/property_stack_builder'
autoload :PropertyStackProcessor, 'penchant/property_stack_processor'
end

View File

@ -1,20 +0,0 @@
module Penchant
class CustomProperty
def initialize(value)
@value = value
end
def process(values)
if @value.respond_to?(:call)
@value.call(*values).to_a
else
@value.collect do |k, v|
v = v.dup.gsub(%r{\$(\d+)}) { |m| values[m.to_i - 1 ] }
[ k, v ]
end
end
end
end
end

View File

@ -1,11 +0,0 @@
module Penchant
class Defaults
def initialize
@defaults = {}
end
def [](key)
@defaults[key.to_s] ||= {}
end
end
end

View File

@ -1,27 +0,0 @@
module Penchant
class DotPenchant
class << self
def run(env = nil, deployment = false)
dot_penchant = new
dot_penchant.run(env)
dot_penchant
end
end
def run(env = nil, deployment = false)
instance_eval(File.read('.penchant'))
end
def rake(*tasks)
command = [ "rake", *tasks ]
command.unshift("bundle exec") if gemfile?
Kernel.system command.join(' ')
end
private
def gemfile?
File.file?('Gemfile')
end
end
end

View File

@ -1,18 +0,0 @@
module Penchant
class Env
attr_accessor :name
def initialize(name)
@name = name.to_s
end
def ==(other)
@name == other.name
end
def to_s
"@#{name}"
end
end
end

View File

@ -1,210 +0,0 @@
module Penchant
class FileProcessor
attr_reader :environment, :is_deployment, :available_environments, :defined_git_repos
ANY_ENVIRONMENT = :any_environment
def self.result(data, *args)
new(data).result(*args)
end
def initialize(data)
@data = data
@available_environments = []
@defined_git_repos = []
@defaults = Defaults.new
@properties = PropertyStackBuilder.new(@defaults)
@_current_env_defaults = {}
end
def result(_env, _is_deployment)
@environment = _env.to_s.to_sym
@is_deployment = _is_deployment
@output = []
instance_eval(@data)
@output.join("\n")
end
def <<(string)
@output << string
end
def env(*args)
options = {}
options = args.pop if args.last.kind_of?(::Hash)
@available_environments += args
requested_env_defaults = _defaults_for(Env.new(environment))
if block_given?
if for_environment?(args)
@_current_env_defaults = requested_env_defaults
yield
@_current_env_defaults = {}
else
if opposite_environment = (options[:opposite] or requested_env_defaults[:opposite])
if for_environment?([ environment, args, opposite_environment ].flatten.uniq)
@_current_env_defaults = requested_env_defaults
@_strip_pathing_options = true
yield
@_strip_pathing_options = false
@_current_env_defaults = {}
end
end
end
else
Env.new(args.shift)
end
end
def property(name, hash = nil, &block)
@properties[name] = hash || block
end
def opposites(left, right)
@defaults[Env.new(left)][:opposite] = right
@defaults[Env.new(right)][:opposite] = left
end
def for_environment?(envs)
envs.include?(environment) || environment == ANY_ENVIRONMENT
end
def no_deployment
yield if !is_deployment
end
def ensure_git_hooks!
Penchant::Hooks.install!
end
def os(*args)
yield if args.include?(current_os)
end
def defaults_for(*args)
defaults = args.pop
args.flatten.each do |gem|
@defaults[gem].merge!(defaults)
end
end
protected
def args_to_string(args)
args.inspect[1..-2]
end
def split_args(args)
template = {}
while args.last.instance_of?(Hash)
template.merge!(args.pop)
end
[ args, template ]
end
def call_and_indent_output(block = nil, &given_block)
index = @output.length
(block || given_block).call
index.upto(@output.length - 1) do |i|
@output[i] = " " + @output[i]
end
end
def _defaults_for(gem_name)
result = @_current_env_defaults
result.merge(@defaults[gem_name] || {})
end
def current_os
require 'rbconfig'
case host_os = RbConfig::CONFIG['host_os']
when /darwin/
:darwin
when /linux/
:linux
else
host_os[%r{^[a-z]+}, 1].to_sym
end
end
def gem(*args)
gem_name = [ args.shift ]
template = {}
if args.last.kind_of?(::Hash)
template = args.pop
end
version = args.first
options = @properties.create_stack_for(template, @_strip_pathing_options).process_for_gem(gem_name.first, @_current_env_defaults)
args = [ gem_name.first ]
args << version if version
if options[:git]
@defined_git_repos << Penchant::Repo.new(options[:git])
end
args << options if !options.empty?
self << %{gem #{args_to_string(args)}}
end
def gems(*args)
gems, template = split_args(args)
gems.flatten.each do |gem_name|
options = @properties.create_stack_for(template, @_strip_pathing_options).process_for_gem(gem_name)
args = [ gem_name ]
args << options if !options.empty?
gem *args
end
end
def group(*args, &block)
self << ""
self << %{group #{args_to_string(args)} do}
call_and_indent_output(block)
self << %{end}
end
def ruby(*args)
passthrough :ruby, *args
end
def bundler_encoding_fix!
self << (<<-RB)
if RUBY_VERSION =~ /1.9/
Encoding.default_external = Encoding::UTF_8
Encoding.default_internal = Encoding::UTF_8
end
RB
end
def gemspec
passthrough :gemspec
end
def source(*args)
passthrough :source, *args
end
def passthrough(method, *args)
self << %{#{method} #{args_to_string(args)}}.strip
end
end
end

View File

@ -1,40 +1,24 @@
require 'erb'
module Penchant
class Gemfile
attr_reader :path, :is_deployment
attr_reader :path
def self.do_full_env_switch!(env, deployment = false)
return false if !(gemfile = pre_switch(env, deployment))
class << self
def do_full_env_switch!(env)
gemfile = Penchant::Gemfile.new
if !gemfile.has_gemfile_erb?
puts "Not using Gemfile.erb, exiting."
return false
end
gemfile.switch_to!(env, deployment)
gemfile.switch_to!(env)
system %{bundle}
end
end
def self.switch_back!(fallback_env)
return false if !(gemfile = pre_switch(fallback_env))
gemfile.switch_back!(fallback_env)
end
def self.pre_switch(env, deployment = false)
gemfile = new
return false if !gemfile.has_processable_gemfile?
gemfile.run_dot_penchant!(env, deployment)
gemfile
end
def self.available_environments
new.available_environments
end
def self.defined_git_repos
new.defined_git_repos
end
def current_env ; @env ; end
def initialize(path = Dir.pwd)
@path = path
@env = environment
end
def gemfile_path
@ -42,75 +26,30 @@ module Penchant
end
def has_gemfile?
File.file?(gemfile_path)
File.file?('Gemfile')
end
def has_dot_penchant?
File.file?('.penchant')
def gemfile_erb_path
file_in_path('Gemfile.erb')
end
def gemfile_penchant_path
file_in_path('Gemfile.penchant')
end
def has_gemfile_penchant?
File.file?(gemfile_penchant_path)
end
def has_processable_gemfile?
has_gemfile_penchant?
end
def processable_gemfile_path
gemfile_penchant_path
def has_gemfile_erb?
File.file?(gemfile_erb_path)
end
def environment
gemfile_header.strip[%r{environment: ([^, ]*)}, 1]
File.readlines(gemfile_path).first.strip[%r{environment: (.*)}, 1]
end
def deployment?
gemfile_header['deployment mode'] != nil
end
def switch_to!(gemfile_env = nil)
@env = gemfile_env
template = File.read(gemfile_erb_path)
def available_environments
process
builder.available_environments
end
File.open(gemfile_path, 'wb') do |fh|
fh.puts "# generated by penchant, environment: #{@env}"
def defined_git_repos
process(FileProcessor::ANY_ENVIRONMENT)
builder.defined_git_repos
end
def switch_to!(gemfile_env = nil, deployment = false)
@env, @is_deployment = gemfile_env, deployment
output = [ header, process ]
File.open(gemfile_path, 'wb') { |fh| fh.print output.join("\n") }
end
def run_dot_penchant!(env, deployment)
DotPenchant.run(env || environment, deployment) if has_dot_penchant?
end
def header
header = [ "# generated by penchant, environment: #{current_env}" ]
if is_deployment
header << ", deployment mode (was #{environment})"
fh.print ERB.new(template).result(binding)
end
header.join
end
def prior_environment
gemfile_header[%r{\(was (.+)\)}, 1]
end
def switch_back!(fallback_env)
switch_to!(prior_environment || fallback_env)
end
private
@ -118,20 +57,8 @@ module Penchant
File.join(@path, file)
end
def process(env = @env)
builder.result(env, @is_deployment)
end
def builder
@builder ||= FileProcessor.new(template)
end
def template
File.read(processable_gemfile_path)
end
def gemfile_header
(has_gemfile? and File.readlines(gemfile_path).first) or ""
def env(check, &block)
instance_eval(&block) if check.to_s == @env.to_s
end
end
end

View File

@ -1,35 +0,0 @@
require 'pathname'
module Penchant
class Hooks
HOOKS_DIR = 'script/hooks'
GIT_HOOKS_DIR = '.git/hooks'
def self.installed?
if File.directory?(HOOKS_DIR)
Dir[File.join(HOOKS_DIR, '*')].each do |file|
target = File.join(GIT_HOOKS_DIR, File.basename(file))
return false if !File.symlink?(target)
return false if !File.expand_path(File.readlink(target)) == File.expand_path(file)
end
end
true
end
def self.install!
if git?
puts "[penchant] installing git hooks"
Dir['script/hooks/*'].each do |hook|
FileUtils.ln_sf File.join(Dir.pwd, hook), "#{GIT_HOOKS_DIR}/#{File.split(hook).last}"
end
end
end
def self.git?
File.directory?(GIT_HOOKS_DIR)
end
end
end

View File

@ -1,28 +0,0 @@
module Penchant
class PropertyStack
PATHING_OPTIONS = [ :git, :branch, :path ].freeze
def initialize(builder, property_stack, strip_pathing_options)
@builder, @property_stack, @strip_pathing_options = builder, property_stack.dup, strip_pathing_options
end
def processor
@processor ||= PropertyStackProcessor.new(@builder)
end
def process_for_gem(gem_name, additional_env = {})
properties = processor.process(gem_name, @property_stack)
if @strip_pathing_options
PATHING_OPTIONS.each { |key| properties.delete(key) }
end
properties = processor.process(gem_name, @builder.defaults[gem_name].merge(additional_env)).merge(properties)
properties.delete(:opposite)
Hash[properties.sort]
end
end
end

View File

@ -1,24 +0,0 @@
module Penchant
class PropertyStackBuilder
attr_reader :defaults
def initialize(defaults)
@defaults = defaults
@custom_properties = {}
end
def []=(key, value)
@custom_properties[key] = CustomProperty.new(value)
end
def [](key)
@custom_properties[key]
end
def create_stack_for(stack, strip_pathing_options = false)
PropertyStack.new(self, stack, strip_pathing_options)
end
end
end

View File

@ -1,26 +0,0 @@
module Penchant
class PropertyStackProcessor
def initialize(builder)
@builder = builder
end
def process(gem_name, stack)
properties = {}
property_stack = stack.dup.to_a
while !property_stack.empty?
key, value = property_stack.shift
if property = @builder[key]
property_stack += property.process([ value ].flatten)
else
value = value % gem_name if value.respond_to?(:%)
properties[key] = value
end
end
properties
end
end
end

View File

@ -1,16 +0,0 @@
module Penchant
class Repo
def initialize(url)
@url = url
end
def clone_to(dir)
Dir.chdir(dir) do
system %{git clone #{@url}}
end
end
def to_s ; @url ; end
end
end

View File

@ -1,3 +1,3 @@
module Penchant
VERSION = "0.2.29"
VERSION = "0.0.1"
end

View File

@ -18,13 +18,4 @@ Gem::Specification.new do |s|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"]
s.add_dependency 'bundler'
s.add_dependency 'thor'
s.add_development_dependency 'cucumber'
s.add_development_dependency 'mocha'
s.add_development_dependency 'fakefs'
s.add_development_dependency 'rspec', '~> 2.6.0'
s.add_development_dependency 'rake'
end

View File

@ -1,11 +0,0 @@
#!/usr/bin/env ruby
require 'rubygems'
require 'penchant'
if Penchant::Gemfile.do_full_env_switch!(ARGV[0])
puts "Gemfile switched to #{ARGV[0]}"
else
exit 0
end

View File

@ -1,27 +0,0 @@
#!/bin/bash
msg=$(cat $1)
# wtf mac os x lion
if [ ! -z "$MY_RUBY_HOME" ]; then
PATH="$MY_RUBY_HOME/bin:$PATH"
fi
if [ ! -z "$GEM_PATH" ]; then
oifs="$IFS"
while IFS=":" read -ra GEM_PATHS; do
FIXED_GEM_PATH=""
for i in "${GEM_PATHS[@]}"; do
FIXED_GEM_PATH="$FIXED_GEM_PATH:${i}/bin"
done
done <<< "$GEM_PATH"
IFS="$oifs"
PATH="$FIXED_GEM_PATH:$PATH"
fi
if [[ "${msg}" != *"[ci skip]"* ]]; then
bundle exec rake --trace
R=$?
if [ $R -ne 0 ]; then exit $R; fi
fi

View File

@ -1,2 +0,0 @@
#!/bin/bash

View File

@ -1,2 +0,0 @@
#!/bin/bash

View File

@ -1,19 +0,0 @@
#!/usr/bin/env ruby
puts "Bundling..."
system %{bundle}
puts "Installing git hooks"
system %{script/install-git-hooks}
bundle = File.file?('Gemfile') ? 'bundle exec' : ''
command = [ bundle, 'rake', '-s', '-T', 'bootstrap' ]
if !(%x{#{command.join(' ')}}).empty?
puts "Trying to run rake bootstrap..."
system %{#{bundle} rake bootstrap}
end
puts "Done!"

View File

@ -1,6 +0,0 @@
#!/bin/bash
for hook in script/hooks/* ; do
ln -sf $PWD/$hook .git/hooks/${hook##*/}
done

View File

@ -0,0 +1,109 @@
require 'spec_helper'
describe Penchant::Gemfile do
include FakeFS::SpecHelpers
let(:dir) { File.expand_path(Dir.pwd) }
let(:gemfile) { described_class.new(dir) }
let(:gemfile_path) { File.join(dir, 'Gemfile') }
let(:gemfile_erb_path) { File.join(dir, 'Gemfile.erb') }
def write_file(path, content = nil)
File.open(path, 'wb') do |fh|
content = yield if block_given?
fh.print content
end
end
subject { gemfile }
context 'with no gemfile' do
it { should_not have_gemfile }
it { should_not have_gemfile_erb }
end
context 'with gemfile' do
let(:data) { "whatever" }
before do
write_file(gemfile_path) { data }
end
describe 'existence' do
it { should have_gemfile }
it { should_not have_gemfile_erb }
end
describe '#environment' do
context 'not defined' do
its(:environment) { should be_nil }
end
context 'defined' do
let(:environment) { 'test' }
let(:data) { <<-GEMFILE }
# generated by penchant, environment: #{environment}
GEMFILE
its(:environment) { should == environment }
end
end
describe '#switch_to!' do
it 'should raise an exception' do
expect { subject.switch_to!(:whatever) }.to raise_error(Errno::ENOENT)
end
end
end
context 'with gemfile.erb' do
let(:erb_data) { 'whatever' }
before do
write_file(gemfile_erb_path) { erb_data }
end
it { should_not have_gemfile }
it { should have_gemfile_erb }
describe '#switch_to!' do
let(:erb_data) { <<-ERB }
<% env :test do %>
test
<% end %>
<% env :not do %>
not
<% end %>
all
ERB
it 'should render test data' do
subject.switch_to!(:test)
File.read('Gemfile').should include('test')
File.read('Gemfile').should_not include('not')
File.read('Gemfile').should include('all')
end
it 'should not render test data' do
subject.switch_to!(:not)
File.read('Gemfile').should_not include('test')
File.read('Gemfile').should include('not')
File.read('Gemfile').should include('all')
end
it 'should not render either' do
subject.switch_to!
File.read('Gemfile').should_not include('test')
File.read('Gemfile').should_not include('not')
File.read('Gemfile').should include('all')
end
end
end
end

View File

6
spec/spec_helper.rb Normal file
View File

@ -0,0 +1,6 @@
require 'fakefs/spec_helpers'
require 'penchant'
RSpec.configure do |c|
c.mock_with :mocha
end

View File

@ -1,40 +0,0 @@
#!/bin/bash
msg=$(cat $1)
OLD_GIT_DIR=$GIT_DIR
# lion appears to insert git paths before everything else. ensure rvm can
# bust through, at the very least.
if [ ! -z "$MY_RUBY_HOME" ]; then
PATH="$MY_RUBY_HOME/bin:$PATH"
fi
if [ ! -z "$GEM_PATH" ]; then
oifs="$IFS"
while IFS=":" read -ra GEM_PATHS; do
FIXED_GEM_PATH=""
for i in "${GEM_PATHS[@]}"; do
FIXED_GEM_PATH="$FIXED_GEM_PATH:${i}/bin"
done
done <<< "$GEM_PATH"
IFS="$oifs"
PATH="$FIXED_GEM_PATH:$PATH"
fi
if [[ "${msg}" != *"[ci skip]"* ]]; then
if [ "$(penchant gemfile-env)" != "remote" ]; then
unset GIT_DIR
penchant gemfile remote
GIT_DIR=$OLD_GIT_DIR
fi
if [ $(bundle exec rake -P | grep default | wc -l) -ne 0 ]; then
bundle exec rake
R=$?
if [ $R -ne 0 ]; then exit $R; fi
else
echo "[penchant] No default Rake task found! Add one if you want to run tests before committing."
fi
fi

View File

@ -1,5 +0,0 @@
#!/bin/bash
unset GIT_DIR
echo "Run penchant gemfile remote to get back to work!"

View File

@ -1,14 +1,12 @@
#!/bin/bash
if [ $(bundle exec rake -P | grep preflight_check | wc -l) -ne 0 ]; then
bundle exec rake preflight_check
R=$?
if [ $R -ne 0 ]; then exit $R; fi
fi
OLD_GIT_DIR=$GIT_DIR
unset GIT_DIR
penchant gemfile remote --deployment
penchant gemfile remote
GIT_DIR=$OLD_GIT_DIR
git add Gemfile*
exit 0
bundle exec rake
R=$?
if [ $R -ne 0 ]; then exit $R; fi

View File

@ -1,7 +1,23 @@
#!/usr/bin/env ruby
puts "Bundling..."
system %{bundle}
if File.file?('Gemfile.erb')
pwd = Dir.pwd
Dir.chdir '..' do
File.readlines(File.join(pwd, 'Gemfile.erb')).find_all { |line| line[':git'] }.each do |line|
repo = line[%r{:git => (['"])(.*)\1}, 2]
puts "Installing #{repo}"
system %{git clone #{repo}}
end
end
puts "Bundling for local environment"
system %{script/gemfile local}
else
puts "Bundling..."
system %{bundle}
end
puts "Installing git hooks"
system %{script/install-git-hooks}