nkryptic-sandbox 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,156 @@
1
+
2
+ require 'fileutils'
3
+ require 'erb'
4
+
5
+ module Sandbox
6
+
7
+ class Installer
8
+
9
+ ## CLASS METHODS
10
+ class << self
11
+ end
12
+ ## END CLASS METHODS
13
+
14
+ attr_accessor :options
15
+
16
+ ## PUBLIC INSTANCE METHODS
17
+ public
18
+
19
+ def initialize( options={} )
20
+ @options = options.dup
21
+ @target = nil
22
+ end
23
+
24
+ def target
25
+ return @target unless @target.nil?
26
+ @target = resolve_target( options[ :target ] )
27
+ end
28
+
29
+ def populate
30
+ puts "creating sandbox at: #{target}" if Sandbox.verbose?
31
+ create_directories
32
+ install_scripts
33
+ install_gems
34
+ end
35
+
36
+ def create_directories
37
+ bin = File.join( target, 'bin' )
38
+ gembin = File.join( target, 'rubygems', 'bin' )
39
+
40
+ # mkdir /path/to/sandbox/
41
+ # mkdir /path/to/sandbox/rubygems
42
+ # mkdir /path/to/sandbox/rubygems/bin
43
+ FileUtils.mkdir_p( gembin )
44
+
45
+ # ln -s /path/to/sandbox/rubygems/bin /path/to/sandbox/bin
46
+ # symlink the bin directory, because when gems are installed, binaries are
47
+ # installed in GEM_HOME/bin
48
+ FileUtils.ln_s( gembin, bin )
49
+ end
50
+
51
+ def install_scripts
52
+ filename = File.join( target, 'bin', 'activate_sandbox' )
53
+ template = File.read( File.dirname( __FILE__ ) + '/templates/activate_sandbox.erb' )
54
+ script = ERB.new( template )
55
+ output = script.result( binding )
56
+ File.open( filename, 'w' ) do |f|
57
+ f.write output
58
+ end
59
+ end
60
+
61
+ def install_gems
62
+ # gem = `which gem`.chomp
63
+ # return if gem.empty?
64
+ puts "installing gems" if Sandbox.verbose?
65
+ begin
66
+ setup_sandbox_env
67
+ options[ :gems ].each do |gem|
68
+ puts " gem: #{gem}" if Sandbox.verbose?
69
+ cmd = "gem install #{gem}"
70
+ # cmd = cmd + ' -V' if Sandbox.really_verbose?
71
+ status, output = shell_out( cmd )
72
+ unless status
73
+ warn "failed to install gem: #{gem}"
74
+ end
75
+ end
76
+ ensure
77
+ restore_sandbox_env
78
+ end
79
+ end
80
+
81
+ def shell_out( cmd )
82
+ # err_capture = Sandbox.really_verbose? '2>&1' : '2>/dev/null'
83
+ # out = `#{cmd} #{err_capture}`
84
+ out = `#{cmd} 2>/dev/null`
85
+ result = $?.exitstatus == 0
86
+ [ result, out ]
87
+ end
88
+
89
+ def setup_sandbox_env
90
+ @old_env = Hash[ *ENV.select { |k,v| ['HOME','GEM_HOME','GEM_PATH'].include?( k ) }.flatten ]
91
+ # @old_env = {}
92
+ # @old_env[ 'HOME' ] = ENV[ 'HOME' ]
93
+ # @old_env[ 'GEM_HOME' ] = ENV[ 'GEM_HOME' ]
94
+ # @old_env[ 'GEM_PATH' ] = ENV[ 'GEM_PATH' ]
95
+
96
+ ENV[ 'HOME' ] = target
97
+ ENV[ 'GEM_HOME' ] = "#{target}/rubygems"
98
+ ENV[ 'GEM_PATH' ] = "#{target}/rubygems"
99
+ end
100
+
101
+ def restore_sandbox_env
102
+ ENV.update( @old_env )
103
+ # ENV[ 'HOME' ] = @old_env[ 'HOME' ]
104
+ # ENV[ 'GEM_HOME' ] = @old_env[ 'GEM_HOME' ]
105
+ # ENV[ 'GEM_PATH' ] = @old_env[ 'GEM_PATH' ]
106
+ end
107
+
108
+ def resolve_target( path )
109
+ # should consider replacing with 'pathname' => Pathname.new( path )
110
+ path = fix_path( path )
111
+ if File.exists?( path )
112
+ raise Sandbox::Error, "target '#{path}' exists"
113
+ end
114
+
115
+ base = path
116
+ while base = File.dirname( base )
117
+ if check_path!( base )
118
+ break
119
+ elsif base == '/'
120
+ break
121
+ end
122
+ end
123
+ return path
124
+ end
125
+
126
+ def check_path!( path )
127
+ if File.directory?( path )
128
+ if File.writable?( path )
129
+ return true
130
+ else
131
+ raise Sandbox::Error, "path '#{path}' has a permission problem"
132
+ end
133
+ elsif File.exists?( path )
134
+ raise Sandbox::Error, "path '#{path}' is not a directory"
135
+ end
136
+ false
137
+ end
138
+
139
+ def fix_path( path )
140
+ unless path.index( '/' ) == 0
141
+ path = File.join( FileUtils.pwd, path )
142
+ end
143
+ path
144
+ end
145
+
146
+ ## END PUBLIC INSTANCE METHODS
147
+
148
+
149
+ ## PRIVATE INSTANCE METHODS
150
+ private
151
+
152
+ ## END PRIVATE INSTANCE METHODS
153
+
154
+ end
155
+
156
+ end
@@ -0,0 +1,91 @@
1
+
2
+ # This file must be used with "source bin/activate" *from bash*
3
+ # you cannot run it directly
4
+
5
+ deactivate_sandbox () {
6
+ if [ -n "$_OLD_SANDBOX_PATH" ] ; then
7
+ PATH="$_OLD_SANDBOX_PATH"
8
+ export PATH
9
+ unset _OLD_SANDBOX_PATH
10
+ fi
11
+
12
+ if [ -n "$_OLD_SANDBOX_HOME" ] ; then
13
+ HOME="$_OLD_SANDBOX_HOME"
14
+ export HOME
15
+ unset _OLD_SANDBOX_HOME
16
+ fi
17
+
18
+ # This should detect bash and zsh, which have a hash command that must
19
+ # be called to get it to forget past commands. Without forgetting
20
+ # past commands the $PATH changes we made may not be respected
21
+ if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then
22
+ hash -r
23
+ fi
24
+
25
+ if [ -n "$_OLD_SANDBOX_GEM_HOME" ] ; then
26
+ GEM_HOME="$_OLD_SANDBOX_GEM_HOME"
27
+ export GEM_HOME
28
+ unset _OLD_SANDBOX_GEM_HOME
29
+ fi
30
+
31
+ if [ -n "$_OLD_SANDBOX_GEM_PATH" ] ; then
32
+ GEM_PATH="$_OLD_SANDBOX_GEM_PATH"
33
+ export GEM_PATH
34
+ unset _OLD_SANDBOX_GEM_PATH
35
+ fi
36
+
37
+ if [ -n "$_OLD_SANDBOX_PS1" ] ; then
38
+ PS1="$_OLD_SANDBOX_PS1"
39
+ export PS1
40
+ unset _OLD_SANDBOX_PS1
41
+ fi
42
+
43
+ # unset again, in case they were missed because they were blank
44
+ unset _OLD_SANDBOX_GEM_HOME
45
+ unset _OLD_SANDBOX_GEM_PATH
46
+
47
+ unset SANDBOX_ENV
48
+ if [ ! "$1" = "nondestructive" ] ; then
49
+ # Self destruct!
50
+ unset deactivate_sandbox
51
+ fi
52
+ }
53
+
54
+ # unset irrelavent variables
55
+ deactivate_sandbox nondestructive
56
+
57
+ SANDBOX_ENV="<%= target %>"
58
+ export SANDBOX_ENV
59
+
60
+ _OLD_SANDBOX_PATH="$PATH"
61
+ PATH="$SANDBOX_ENV/bin:$PATH"
62
+ export PATH
63
+
64
+ _OLD_SANDBOX_HOME="$HOME"
65
+ HOME="$SANDBOX_ENV"
66
+ export HOME
67
+
68
+ _OLD_SANDBOX_GEM_HOME="$GEM_HOME"
69
+ GEM_HOME="$SANDBOX_ENV/rubygems"
70
+ export GEM_HOME
71
+
72
+ _OLD_SANDBOX_GEM_PATH="$GEM_PATH"
73
+ GEM_PATH="$GEM_HOME"
74
+ export GEM_PATH
75
+
76
+ _OLD_SANDBOX_PS1="$PS1"
77
+ if [ "`basename \"$SANDBOX_ENV\"`" = "__" ] ; then
78
+ # special case for Aspen magic directories
79
+ # see http://www.zetadev.com/software/aspen/
80
+ PS1="[sandbox:`basename \`dirname \"$SANDBOX_ENV\"\``] $PS1"
81
+ else
82
+ PS1="(sandbox:`basename \"$SANDBOX_ENV\"`)$PS1"
83
+ fi
84
+ export PS1
85
+
86
+ # This should detect bash and zsh, which have a hash command that must
87
+ # be called to get it to forget past commands. Without forgetting
88
+ # past commands the $PATH changes we made may not be respected
89
+ if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then
90
+ hash -r
91
+ fi
@@ -0,0 +1,15 @@
1
+
2
+ module Sandbox
3
+
4
+ class Version #:nodoc:
5
+
6
+ MAJOR = 0
7
+ MINOR = 2
8
+ TINY = 0
9
+
10
+ STRING = [ MAJOR, MINOR, TINY ].join( '.' )
11
+
12
+ end
13
+
14
+ end
15
+
data/lib/sandbox.rb ADDED
@@ -0,0 +1,33 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless \
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+
5
+ module Sandbox
6
+
7
+ class << self
8
+
9
+ def verbosity
10
+ @verbosity ||= 0
11
+ end
12
+
13
+ def increase_verbosity
14
+ @verbosity = verbosity + 1
15
+ end
16
+
17
+ def decrease_verbosity
18
+ @verbosity = verbosity - 1
19
+ end
20
+
21
+ def quiet?() verbosity < 0 end
22
+ def really_quiet?() verbosity < 1 end
23
+ def verbose?() verbosity > 0 end
24
+ def really_verbose?() verbosity > 1 end
25
+
26
+ end
27
+
28
+ end
29
+
30
+ require 'sandbox/version'
31
+ require 'sandbox/errors'
32
+ require 'sandbox/installer'
33
+
data/sandbox.gemspec ADDED
@@ -0,0 +1,43 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{sandbox}
5
+
6
+ s.version = "0.2.0"
7
+
8
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
9
+ s.authors = ["Jacob Radford"]
10
+ s.date = %q{2008-12-01}
11
+ s.default_executable = %q{sandbox}
12
+ s.description = %q{Create virtual ruby/rubygems sandboxes.}
13
+ s.email = %q{nkryptic@gmail.com}
14
+ s.executables = ["sandbox"]
15
+ s.extra_rdoc_files = ["CHANGELOG", "TODO", "README.rdoc", "tasks/rspec.rake", "tasks/cucumber.rake", "tasks/gem.rake", "tasks/dcov.rake", "lib/sandbox.rb", "lib/sandbox/installer.rb", "lib/sandbox/cli.rb", "lib/sandbox/templates/activate_sandbox.erb", "lib/sandbox/version.rb", "lib/sandbox/errors.rb", "bin/sandbox"]
16
+ s.files = ["sandbox.gemspec", "CHANGELOG", "TODO", "spec/sandbox/cli_spec.rb", "spec/sandbox/installer_spec.rb", "spec/sandbox_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "Manifest", "features/steps/common.rb", "features/steps/env.rb", "features/development.feature", "README.rdoc", "tasks/rspec.rake", "tasks/cucumber.rake", "tasks/gem.rake", "tasks/dcov.rake", "Rakefile", "lib/sandbox.rb", "lib/sandbox/installer.rb", "lib/sandbox/cli.rb", "lib/sandbox/templates/activate_sandbox.erb", "lib/sandbox/version.rb", "lib/sandbox/errors.rb", "bin/sandbox"]
17
+ s.has_rdoc = true
18
+ s.homepage = %q{http://github.com/nkryptic/sandbox}
19
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Sandbox", "--main", "README.rdoc"]
20
+ s.require_paths = ["lib"]
21
+ s.rubyforge_project = %q{sandbox}
22
+ s.rubygems_version = %q{1.3.1}
23
+ s.summary = %q{Create virtual ruby/rubygems sandboxes.}
24
+
25
+ if s.respond_to? :specification_version then
26
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
27
+ s.specification_version = 2
28
+
29
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
30
+ s.add_development_dependency(%q<echoe>, ["~> 0", "= 3.0"])
31
+ s.add_development_dependency(%q<rspec>, ["~> 0", "= 1.1.0"])
32
+ s.add_development_dependency(%q<mocha>, ["~> 0", "= 0.9"])
33
+ else
34
+ s.add_dependency(%q<echoe>, ["~> 0", "= 3.0"])
35
+ s.add_dependency(%q<rspec>, ["~> 0", "= 1.1.0"])
36
+ s.add_dependency(%q<mocha>, ["~> 0", "= 0.9"])
37
+ end
38
+ else
39
+ s.add_dependency(%q<echoe>, ["~> 0", "= 3.0"])
40
+ s.add_dependency(%q<rspec>, ["~> 0", "= 1.1.0"])
41
+ s.add_dependency(%q<mocha>, ["~> 0", "= 0.9"])
42
+ end
43
+ end
@@ -0,0 +1,199 @@
1
+
2
+ require File.dirname( __FILE__ ) + '/../spec_helper'
3
+ require 'sandbox/cli'
4
+
5
+ describe Sandbox::CLI do
6
+
7
+ it "should raise an error when running from a loaded sandbox" do
8
+ begin
9
+ ENV[ 'SANDBOX' ] = 'something'
10
+ lambda { Sandbox::CLI.verify_environment! }.should raise_error( Sandbox::LoadedSandboxError )
11
+ ensure
12
+ ENV[ 'SANDBOX' ] = nil
13
+ end
14
+ end
15
+
16
+ describe "calling execute" do
17
+ before( :each ) do
18
+ @instance = stub_everything()
19
+ end
20
+
21
+ it "should handle all errors" do
22
+ Sandbox::CLI.stubs( :new ).raises( StandardError )
23
+ Sandbox::CLI.expects( :handle_error ).with( instance_of( StandardError ) )
24
+ Sandbox::CLI.execute
25
+ end
26
+
27
+ it "should attempt to parse ARGV by default" do
28
+ Sandbox::CLI.expects( :parse ).with( ARGV ).returns( @instance )
29
+ Sandbox::CLI.execute
30
+ end
31
+
32
+ it "should create a new instance" do
33
+ Sandbox::CLI.expects( :new ).returns( @instance )
34
+ Sandbox::CLI.execute
35
+ end
36
+
37
+ it "should call parse_args! on the new instance" do
38
+ @instance.expects( :parse_args! )
39
+ Sandbox::CLI.expects( :new ).returns( @instance )
40
+ Sandbox::CLI.execute
41
+ end
42
+
43
+ it "should run the instance" do
44
+ @instance.expects( :execute! )
45
+ Sandbox::CLI.expects( :parse ).returns( @instance )
46
+ Sandbox::CLI.execute
47
+ end
48
+ end
49
+
50
+ describe "a new instance" do
51
+
52
+ before( :each ) do
53
+ @cli = Sandbox::CLI.new
54
+ end
55
+
56
+ it "should have default 'gems to install'" do
57
+ @cli.options[ :gems ] = [ 'rake' ]
58
+ end
59
+
60
+ describe "instance calling parse_args!" do
61
+ def processor( *args ); lambda { @cli.parse_args!( args ) }; end
62
+ def process( *args ); processor( *args ).call; end
63
+
64
+ describe "using NO arguments" do
65
+ it "should use raise nothing" do
66
+ process()
67
+ @cli.options[ :original_args ].should == []
68
+ @cli.options[ :args ].should == []
69
+ end
70
+ end
71
+
72
+ describe "using VALID arguments" do
73
+ [ '-V', '--version' ].each do |arg|
74
+ it "should print the version for switch '#{arg}'" do
75
+ @cli.expects( :puts ).with( Sandbox::Version::STRING )
76
+ processor( arg ).should raise_error( SystemExit ) { |error| error.status.should == 0 }
77
+ end
78
+ end
79
+
80
+ [ '-V', '--version' ].each do |arg|
81
+ it "should ignore additional arguments after '#{arg}'" do
82
+ # @cli.stubs(:puts)
83
+ @cli.expects( :puts ).with( Sandbox::Version::STRING ).times(2)
84
+ processor( arg, '-x' ).should raise_error( SystemExit ) { |error| error.status.should == 0 }
85
+ processor( arg, 'unknown' ).should raise_error( SystemExit ) { |error| error.status.should == 0 }
86
+ end
87
+ end
88
+
89
+ [ '-h', '--help' ].each do |arg|
90
+ it "should show help for '#{arg}'" do
91
+ @cli.expects( :puts ).with( instance_of( OptionParser ) )
92
+ processor( arg ).should raise_error( SystemExit ) { |error| error.status.should == 0 }
93
+ end
94
+ end
95
+
96
+ [ '-h', '--help' ].each do |arg|
97
+ it "should ignore additional arguments after '#{arg}'" do
98
+ @cli.expects( :puts ).with( instance_of( OptionParser ) ).times(2)
99
+ processor( arg, '-x' ).should raise_error( SystemExit ) { |error| error.status.should == 0 }
100
+ processor( arg, 'unknown' ).should raise_error( SystemExit ) { |error| error.status.should == 0 }
101
+ end
102
+ end
103
+
104
+ [ '-H', '--long-help' ].each do |arg|
105
+ it "should show long help for '#{arg}'" do
106
+ @cli.expects( :puts )
107
+ @cli.expects( :long_help )
108
+ processor( arg ).should raise_error( SystemExit ) { |error| error.status.should == 0 }
109
+ end
110
+ end
111
+
112
+ [ '-v', '--verbose' ].each do |arg|
113
+ it "should increase verbosity with '#{arg}'" do
114
+ Sandbox.expects( :increase_verbosity )
115
+ process( arg )
116
+ @cli.options[ :original_args ].should == [ arg ]
117
+ @cli.options[ :args ].should == []
118
+ end
119
+ end
120
+
121
+ [ [ '-v', '-v' ], [ '--verbose', '--verbose' ] ].each do |args|
122
+ it "should increase verbosity twice with '#{args.join(' ')}'" do
123
+ Sandbox.expects( :increase_verbosity ).times(2)
124
+ process( *args )
125
+ @cli.options[ :original_args ].should == args
126
+ @cli.options[ :args ].should == []
127
+ end
128
+ end
129
+
130
+ it "should require additional arguments with switch '-g'" do
131
+ processor( '-g' ).should raise_error( Sandbox::ParseError )
132
+ end
133
+
134
+ it "should set 'gems to install' with switch '-g'" do
135
+ args = [ '-g', 'somegem,anothergem' ]
136
+ process( *args )
137
+ @cli.options[ :original_args ].should == args
138
+ @cli.options[ :args ].should == []
139
+ @cli.options[ :gems ].should == [ 'somegem', 'anothergem' ]
140
+ end
141
+
142
+ it "should clear 'gems to install' with switch '-n'" do
143
+ process( '-n' )
144
+ @cli.options[ :original_args ].should == [ '-n' ]
145
+ @cli.options[ :args ].should == []
146
+ @cli.options[ :gems ].should == []
147
+ end
148
+
149
+ it "should store leftover arguments in options for arguments '/path/to/somewhere'" do
150
+ args = [ '/path/to/somewhere' ]
151
+ process( *args )
152
+ @cli.options[ :original_args ].should == args
153
+ @cli.options[ :args ].should == args
154
+ end
155
+
156
+ it "should store leftover arguments in options for arguments '-v /path/to/somewhere'" do
157
+ args = [ '-v', '/path/to/somewhere' ]
158
+ process( *args )
159
+ @cli.options[ :original_args ].should == args
160
+ @cli.options[ :args ].should == [ args.last ]
161
+ end
162
+ end
163
+
164
+ describe "using INVALID arguments" do
165
+ it "should exit with message for invalid switch '-x'" do
166
+ processor( '-x' ).
167
+ should raise_error( Sandbox::ParseError ) { |error| error.message.should =~ /-x\b/ }
168
+ end
169
+ end
170
+ end
171
+
172
+ describe "instance calling execute!" do
173
+ def processor; lambda { @cli.execute! }; end
174
+ def process; processor.call; end
175
+
176
+ it "should raise error with no target specified" do
177
+ @cli.options[ :args ] = []
178
+ processor.should raise_error
179
+ end
180
+
181
+ it "should raise error with more than one target" do
182
+ @cli.options[ :args ] = [ 'one', 'two' ]
183
+ processor.should raise_error
184
+ end
185
+
186
+ it "should instantiate an Installer and call populate with one target" do
187
+ @cli.options.delete( :gems )
188
+ @cli.options[ :args ] = [ 'one' ]
189
+ installer = mock( 'Installer', :populate )
190
+ Sandbox::Installer.expects( :new ).with( { :target => 'one' } ).returns( installer )
191
+ process
192
+ end
193
+
194
+ end
195
+
196
+ end
197
+
198
+ end
199
+