clamshell 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +22 -0
- data/README.md +126 -0
- data/Rakefile +4 -0
- data/bin/clamshell +14 -0
- data/clamshell.gemspec +27 -0
- data/lib/clamshell.rb +19 -0
- data/lib/clamshell/cli.rb +41 -0
- data/lib/clamshell/dsl.rb +8 -0
- data/lib/clamshell/environment.rb +94 -0
- data/lib/clamshell/version.rb +3 -0
- data/spec/clamshell/cli_spec.rb +73 -0
- data/spec/clamshell/environment_spec.rb +194 -0
- data/spec/fixtures/Shell.env +24 -0
- data/spec/spec_helper.rb +23 -0
- data/tasks/rspec.rake +22 -0
- metadata +112 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.1.2)
|
5
|
+
rake (0.8.7)
|
6
|
+
rspec (2.4.0)
|
7
|
+
rspec-core (~> 2.4.0)
|
8
|
+
rspec-expectations (~> 2.4.0)
|
9
|
+
rspec-mocks (~> 2.4.0)
|
10
|
+
rspec-core (2.4.0)
|
11
|
+
rspec-expectations (2.4.0)
|
12
|
+
diff-lcs (~> 1.1.2)
|
13
|
+
rspec-mocks (2.4.0)
|
14
|
+
thor (0.14.6)
|
15
|
+
|
16
|
+
PLATFORMS
|
17
|
+
ruby
|
18
|
+
|
19
|
+
DEPENDENCIES
|
20
|
+
rake
|
21
|
+
rspec
|
22
|
+
thor
|
data/README.md
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
# Clamshell
|
2
|
+
|
3
|
+
Clamshell is a tool that converts generic shell statements into shell specific
|
4
|
+
statements that can be sourced to set up an environment.
|
5
|
+
|
6
|
+
## Motivation
|
7
|
+
|
8
|
+
While working on a legacy project that used tcsh as its primary shell, I wanted
|
9
|
+
to use bash, but realized that some of the old schoolers actually liked tcsh.
|
10
|
+
This was the compromise.
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
gem install clamshell
|
15
|
+
|
16
|
+
## Setting up an environment file
|
17
|
+
|
18
|
+
Sometimes your project has a dependency that is shell specific (environment variables,
|
19
|
+
aliases). Setup a `Shell.env` file in your project root with the following:
|
20
|
+
|
21
|
+
Environment.setup ("bash") do
|
22
|
+
env_var "LC_CTYPE", "en_US"
|
23
|
+
env_var "PATH", :prepend => "~/bin", :delimiter => ":"
|
24
|
+
env_var "PATH", :append => "/usr/bin", :delimiter => ":"
|
25
|
+
env_alias editor "vim"
|
26
|
+
end
|
27
|
+
|
28
|
+
You can convert these statements to bash statements as follows:
|
29
|
+
|
30
|
+
clamshell convert SHELL.env
|
31
|
+
|
32
|
+
which will print the following to standard out (or to a file using the `--shell-out=FILE` flag).
|
33
|
+
|
34
|
+
export LC_CTYPE=en_US
|
35
|
+
export PATH=~/bin:$PATH
|
36
|
+
export PATH=$PATH:/usr/bin
|
37
|
+
alias editor=vim
|
38
|
+
|
39
|
+
### Shell independence
|
40
|
+
|
41
|
+
Your environment file doesn't even need to specify a shell:
|
42
|
+
|
43
|
+
Environment.setup do
|
44
|
+
...
|
45
|
+
end
|
46
|
+
|
47
|
+
But you must pass the flag `--shell=SHELLNAME`.
|
48
|
+
Best practices for multi-shell environments use the following command:
|
49
|
+
|
50
|
+
--shell=`ps -p $$ | awk 'NR==2 {print $4}'`
|
51
|
+
|
52
|
+
which will set the shell flag to the type of shell currently being ran.
|
53
|
+
|
54
|
+
Currently, the shells supported are tcsh and bash. However, I am assuming that
|
55
|
+
csh and zsh are supported as well since they are closely related to tcsh and
|
56
|
+
bash, respectively. Hence, aliases are set up for their respective shells.
|
57
|
+
|
58
|
+
### Generic shell statements
|
59
|
+
|
60
|
+
You can also call generic shell statements that are valid in all shells:
|
61
|
+
|
62
|
+
echo -n FOO
|
63
|
+
|
64
|
+
But you must be verbose that you are doing so:
|
65
|
+
|
66
|
+
cmd "echo -n FOO"
|
67
|
+
|
68
|
+
### Unique shell statements
|
69
|
+
|
70
|
+
If you need to setup the environment and produce a shell command that is
|
71
|
+
different depending on the shell being used, the global variable `$SHELL`
|
72
|
+
is available:
|
73
|
+
|
74
|
+
Environment.setup do
|
75
|
+
if $SHELL == "tcsh"
|
76
|
+
cmd "unlimit coredumpsize"
|
77
|
+
elsif $SHELL == "bash"
|
78
|
+
cmd "ulimit -c unlimited"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
### Splitting the environment
|
83
|
+
|
84
|
+
If you want to split your environment files up, the `include_file` command
|
85
|
+
is as your disposal.
|
86
|
+
|
87
|
+
Environment.setup do
|
88
|
+
include_file "/path/to/another.file"
|
89
|
+
end
|
90
|
+
|
91
|
+
`another.file`'s contents:
|
92
|
+
|
93
|
+
env_var "CLASSPATH", :append => "~/java"
|
94
|
+
cmd "echo FOOBAR"
|
95
|
+
|
96
|
+
If you definitely need to split your environment up, take this approach.
|
97
|
+
|
98
|
+
#### Pitfall
|
99
|
+
|
100
|
+
Another approach you might have considered is to generate multiple
|
101
|
+
files and source each one.
|
102
|
+
|
103
|
+
__DON'T!__
|
104
|
+
|
105
|
+
In tcsh, appending to an environment variable that doesn't exist throws an
|
106
|
+
error. There is an internal mechanism in clamshell that detects if an
|
107
|
+
environment variable doesn't exist. If it doesn't it creates one and sets
|
108
|
+
it to an empty string before appending.
|
109
|
+
|
110
|
+
So the previous example would generate the following statements:
|
111
|
+
|
112
|
+
setenv CLASSPATH ""
|
113
|
+
setenv CLASSPATH ${CLASSPATH}:~/java
|
114
|
+
echo FOOBAR
|
115
|
+
|
116
|
+
This can cause some headaches if more than one file is generated.
|
117
|
+
|
118
|
+
*Rule of thumb: generate one file, source one file.*
|
119
|
+
|
120
|
+
### Conversion on the fly
|
121
|
+
|
122
|
+
If you need to convert a generic statement without using a file, you can
|
123
|
+
use the `convert_string` action, but you must specify a shell.
|
124
|
+
|
125
|
+
clamshell convert_string "env_var 'FOO' 'BAR'" --shell=tcsh
|
126
|
+
=> setenv FOO BAR\n
|
data/Rakefile
ADDED
data/bin/clamshell
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + '/../lib/clamshell'
|
4
|
+
require File.dirname(__FILE__) + '/../lib/clamshell/cli'
|
5
|
+
|
6
|
+
begin
|
7
|
+
Clamshell::CLI.start
|
8
|
+
rescue StandardError => e
|
9
|
+
puts e.message
|
10
|
+
exit 1
|
11
|
+
rescue Interrupt => e
|
12
|
+
puts "\nQuitting..."
|
13
|
+
exit 1
|
14
|
+
end
|
data/clamshell.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "clamshell/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "clamshell"
|
7
|
+
s.version = Clamshell::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Eric Thomas"]
|
10
|
+
s.email = ["eric.l.m.thomas@gmail.com"]
|
11
|
+
s.homepage = "https://github.com/et/clamshell"
|
12
|
+
s.summary = %q{Clamshell manages your environment in a shell-independent setup.}
|
13
|
+
s.description = %q{Clamshell allows you to setup shell statements which configure
|
14
|
+
your project's environment that doesn't care about which shell
|
15
|
+
(bash, tcsh) the target user will be using.}
|
16
|
+
|
17
|
+
s.rubyforge_project = "clamshell"
|
18
|
+
|
19
|
+
s.add_dependency('thor')
|
20
|
+
|
21
|
+
s.add_development_dependency('rspec')
|
22
|
+
|
23
|
+
s.files = `git ls-files`.split("\n")
|
24
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
25
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
26
|
+
s.require_paths = ["lib"]
|
27
|
+
end
|
data/lib/clamshell.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
module Clamshell
|
5
|
+
|
6
|
+
autoload :Dsl, 'clamshell/dsl'
|
7
|
+
autoload :Environment, 'clamshell/environment'
|
8
|
+
|
9
|
+
class SafeExit < StandardError; end
|
10
|
+
class DslError < StandardError; end
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_writer :settings
|
14
|
+
|
15
|
+
def settings
|
16
|
+
@settings ||= {}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Clamshell
|
4
|
+
class CLI < Thor
|
5
|
+
|
6
|
+
def initialize(*)
|
7
|
+
super
|
8
|
+
Clamshell.settings = options
|
9
|
+
end
|
10
|
+
|
11
|
+
desc "convert FILE", "Converts an environment file to shell statements"
|
12
|
+
method_option :shell, :type => :string, :banner => "Shell to create statements for"
|
13
|
+
method_option :shell_out, :type => :string, :banner => "File to output to"
|
14
|
+
def convert(file)
|
15
|
+
check_file(file)
|
16
|
+
|
17
|
+
file_out = Clamshell.settings[:shell_out]
|
18
|
+
if file_out
|
19
|
+
File.open(file_out, "w") {|f| f.write(Dsl.build(file) + "\n") }
|
20
|
+
else
|
21
|
+
puts Dsl.build(file)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "convert_string STRING", "Converts a string on the fly."
|
26
|
+
method_option :shell, :type => :string, :banner => "Shell to create statements for", :required => true
|
27
|
+
def convert_string(string)
|
28
|
+
input = <<-I
|
29
|
+
Environment.setup do
|
30
|
+
#{string}
|
31
|
+
end
|
32
|
+
I
|
33
|
+
puts instance_eval(input).inspect
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def check_file(file)
|
38
|
+
abort("File: #{file}, not found.") unless File.exist?(file)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Clamshell
|
2
|
+
class Environment
|
3
|
+
|
4
|
+
attr_reader :shell
|
5
|
+
|
6
|
+
def self.setup(shell = nil, &block)
|
7
|
+
$SHELL = shell ||= Clamshell.settings[:shell]
|
8
|
+
raise "No shell specified" unless shell
|
9
|
+
|
10
|
+
e = new(shell)
|
11
|
+
e.instance_eval(&block)
|
12
|
+
return e
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(shell)
|
16
|
+
@shell = case shell
|
17
|
+
when "csh", "tcsh" then TcshAdapter
|
18
|
+
when "zsh", "bash" then BashAdapter
|
19
|
+
else
|
20
|
+
raise "Unsupported shell"
|
21
|
+
end
|
22
|
+
@stmts = []
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
def env_var(name, *args)
|
27
|
+
case
|
28
|
+
when args[0].class == String # direct assignment
|
29
|
+
@stmts << @shell.env_var(name, args[0])
|
30
|
+
|
31
|
+
else # appending or prepending to existing variable
|
32
|
+
|
33
|
+
# In csh, concatting to an undefined environment variable
|
34
|
+
# results in an error. This is a safe guard that sets the
|
35
|
+
# environment variable to an empty string before appending/prepending.
|
36
|
+
unless ENV[name]
|
37
|
+
ENV[name] = %q{""}
|
38
|
+
@stmts << @shell.env_var(name, ENV[name])
|
39
|
+
end
|
40
|
+
|
41
|
+
delimiter = args[0][:delimiter] || ""
|
42
|
+
if args[0][:prepend]
|
43
|
+
val = args[0][:prepend] + delimiter + "${#{name}}"
|
44
|
+
elsif args[0][:append]
|
45
|
+
val = "${#{name}}" + delimiter + args[0][:append]
|
46
|
+
else
|
47
|
+
raise DslError, "Must specify prepend or append"
|
48
|
+
end
|
49
|
+
@stmts << @shell.env_var(name, val)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def env_alias(name, val)
|
54
|
+
@stmts << @shell.env_alias(name, val)
|
55
|
+
end
|
56
|
+
|
57
|
+
def cmd(stmt)
|
58
|
+
@stmts << stmt
|
59
|
+
end
|
60
|
+
|
61
|
+
def include_file(file)
|
62
|
+
instance_eval File.read(file), file
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_s
|
66
|
+
@stmts.join("\n")
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
def quote(val)
|
71
|
+
%Q{"#{val}"}
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class BashAdapter
|
76
|
+
def self.env_var(name, val)
|
77
|
+
"export #{name}=#{val}"
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.env_alias(name, val)
|
81
|
+
"alias #{name}=#{val}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class TcshAdapter
|
86
|
+
def self.env_var(name, val)
|
87
|
+
"setenv #{name} #{val}"
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.env_alias(name, val)
|
91
|
+
"alias #{name} #{val}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'clamshell/cli'
|
5
|
+
|
6
|
+
describe Clamshell::CLI do
|
7
|
+
|
8
|
+
# explicit subject
|
9
|
+
subject { Clamshell::CLI }
|
10
|
+
|
11
|
+
after :each do
|
12
|
+
Clamshell.settings = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#initialize" do
|
16
|
+
|
17
|
+
it "shows the help listing with no args" do
|
18
|
+
capture(:stdout){ subject.start }.should =~ /Tasks:/
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#convert" do
|
23
|
+
before do
|
24
|
+
@action = "convert"
|
25
|
+
@file_in = "#{FIXTURES_DIR}/Shell.env"
|
26
|
+
end
|
27
|
+
|
28
|
+
it "shows an error for a file not found" do
|
29
|
+
test_missing_file([@action, "missing_file"])
|
30
|
+
end
|
31
|
+
|
32
|
+
it "#--shell, it should set a shell option" do
|
33
|
+
capture(:stdout){ subject.start([@action, @file_in, "--shell=bash"])}
|
34
|
+
Clamshell.settings[:shell].should == "bash"
|
35
|
+
end
|
36
|
+
|
37
|
+
context "output" do
|
38
|
+
it "should print to standard out" do
|
39
|
+
capture(:stdout){ subject.start [@action, @file_in]}.should_not be_empty
|
40
|
+
end
|
41
|
+
|
42
|
+
it "#--shell-out, should output to a file" do
|
43
|
+
file = mock('file')
|
44
|
+
File.should_receive(:open).with("filename", "w").and_yield(file)
|
45
|
+
file.should_receive(:write)
|
46
|
+
subject.start ["convert", @file_in, "--shell-out=filename"]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#convert_string" do
|
52
|
+
before do
|
53
|
+
@action = "convert_string"
|
54
|
+
@string_in = %q{env_var "FOO", "BAR"}
|
55
|
+
end
|
56
|
+
|
57
|
+
it "show an error when --shell is not used" do
|
58
|
+
capture(:stderr) {
|
59
|
+
subject.start [@action, @string_in]
|
60
|
+
}.should =~ /No value provided for required options '--shell'/
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should convert a string" do
|
64
|
+
capture(:stdout) {
|
65
|
+
subject.start [@action, @string_in, "--shell=tcsh"]
|
66
|
+
}.should == "setenv FOO BAR\n"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_missing_file(argv)
|
72
|
+
expect { capture(:stderr){ subject.start(argv)}}.to raise_error(SystemExit, /File: \S*, not found/)
|
73
|
+
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Clamshell::Environment do
|
4
|
+
|
5
|
+
describe "setup" do
|
6
|
+
it "should raise an error when no shell is given" do
|
7
|
+
expect { Clamshell::Environment.setup( & proc{})}.to raise_error(RuntimeError, /No shell specified/)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should return an Environment object" do
|
11
|
+
Clamshell::Environment.setup("bash", & proc{}).should be_an_instance_of Clamshell::Environment
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "shell initializer" do
|
16
|
+
it "should set up a tcsh shell" do
|
17
|
+
Clamshell::Environment.new("tcsh").shell.should be Clamshell::TcshAdapter
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should set up a csh(tcsh) shell" do
|
21
|
+
Clamshell::Environment.new("csh").shell.should be Clamshell::TcshAdapter
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should set up a bash shell" do
|
25
|
+
Clamshell::Environment.new("bash").shell.should be Clamshell::BashAdapter
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should set up a zsh(bash) shell" do
|
29
|
+
Clamshell::Environment.new("zsh").shell.should be Clamshell::BashAdapter
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should raise an error on an unknown shell" do
|
33
|
+
expect { Clamshell::Environment.new("sea")}.to raise_error(RuntimeError, /Unsupported shell/)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'shell specific' do
|
38
|
+
before :each do
|
39
|
+
@bash = Clamshell::Environment.new("bash")
|
40
|
+
@tcsh = Clamshell::Environment.new("tcsh")
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "env_alias" do
|
44
|
+
it "should set a tcsh alias" do
|
45
|
+
@tcsh.env_alias("FOO", "BAR")
|
46
|
+
@tcsh.inspect.should == %q{alias FOO BAR}
|
47
|
+
end
|
48
|
+
it "should set a bash alias" do
|
49
|
+
@bash.env_alias("FOO", "BAR")
|
50
|
+
@bash.inspect.should == %q{alias FOO=BAR}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "environment variable" do
|
55
|
+
describe "set" do
|
56
|
+
|
57
|
+
it "should set up a bash environment variable" do
|
58
|
+
@bash.env_var("FOO", "BAR")
|
59
|
+
@bash.inspect.should == "export FOO=BAR"
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should set up a tcsh environment variable" do
|
63
|
+
@tcsh.env_var("FOO", "BAR")
|
64
|
+
@tcsh.inspect.should == "setenv FOO BAR"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "concat" do
|
69
|
+
|
70
|
+
before :all do
|
71
|
+
ENV['FOO'] = 'FOO'
|
72
|
+
end
|
73
|
+
|
74
|
+
after :all do
|
75
|
+
ENV['FOO'] = nil
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "prepend" do
|
79
|
+
it "should prepend to a bash environment variable" do
|
80
|
+
@bash.env_var("FOO", :prepend => "BAR")
|
81
|
+
@bash.inspect.should == "export FOO=BAR${FOO}"
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should prepend to a tcsh environment variable" do
|
85
|
+
@tcsh.env_var("FOO", :prepend => "BAR")
|
86
|
+
@tcsh.inspect.should == "setenv FOO BAR${FOO}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "append" do
|
91
|
+
it "should append to a bash environment variable" do
|
92
|
+
@bash.env_var("FOO", :append => "BAR")
|
93
|
+
@bash.inspect.should == %q{export FOO=${FOO}BAR}
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should append to a tcsh environment variable" do
|
97
|
+
@tcsh.env_var("FOO", :append => "BAR")
|
98
|
+
@tcsh.inspect.should == %q{setenv FOO ${FOO}BAR}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context "safe concat" do
|
103
|
+
before :each do
|
104
|
+
ENV['UNDEFINED'] = nil
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should set the environment variable to empty string if not set" do
|
108
|
+
@tcsh.env_var("UNDEFINED", :append => "FOO")
|
109
|
+
@tcsh.inspect.should == %Q{setenv UNDEFINED ""\nsetenv UNDEFINED ${UNDEFINED}FOO}
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should not try to keep setting the environment variable" do
|
113
|
+
@tcsh.env_var("UNDEFINED", :append => "FOO")
|
114
|
+
@tcsh.env_var("UNDEFINED", :append => "BAR")
|
115
|
+
@tcsh.inspect.should == %Q{setenv UNDEFINED ""\nsetenv UNDEFINED ${UNDEFINED}FOO\nsetenv UNDEFINED ${UNDEFINED}BAR}
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "delimiter" do
|
121
|
+
it "should raise an error when prepend/append not defined" do
|
122
|
+
expect { @bash.env_var("FOO", :delimiter => ":")}.to raise_error(Clamshell::DslError, /Must specify prepend or append/)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should use a delimiter to append a variable" do
|
126
|
+
@bash.env_var("FOO", :append => "BAR", :delimiter => ":")
|
127
|
+
@bash.inspect.should == %q{export FOO=${FOO}:BAR}
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
describe "generic statement" do
|
134
|
+
it "should print out what it receives" do
|
135
|
+
@bash.cmd "echo blah"
|
136
|
+
@bash.inspect.should == %q{echo blah}
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe "include_file" do
|
141
|
+
it "should include a file" do
|
142
|
+
input = <<-I.gsub(/^\s+/, "").chop
|
143
|
+
env_var "FOO", "BAR"
|
144
|
+
cmd "echo blah"
|
145
|
+
I
|
146
|
+
File.stub!(:read).with("included_file.txt").and_return(input)
|
147
|
+
@bash.include_file("included_file.txt")
|
148
|
+
@bash.inspect.should == %Q{export FOO=BAR\necho blah}
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe "to_s" do
|
154
|
+
it "should convert a block" do
|
155
|
+
block = proc {
|
156
|
+
env_var "FOO", "BAR"
|
157
|
+
env_var "BAZ", "BUZZ"
|
158
|
+
}
|
159
|
+
|
160
|
+
out = <<-O.gsub(/^\s+/, "").chop
|
161
|
+
export FOO=BAR
|
162
|
+
export BAZ=BUZZ
|
163
|
+
O
|
164
|
+
|
165
|
+
Clamshell::Environment.setup("bash", &block).inspect.should == out
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should convert a block with generic statements" do
|
169
|
+
block = proc {
|
170
|
+
env_var "FOO", "BAR"
|
171
|
+
cmd "echo -n blah"
|
172
|
+
env_var "BAZ", "BUZZ"
|
173
|
+
}
|
174
|
+
|
175
|
+
out = <<-O.gsub(/^\s+/, "").chop
|
176
|
+
setenv FOO BAR
|
177
|
+
echo -n blah
|
178
|
+
setenv BAZ BUZZ
|
179
|
+
O
|
180
|
+
Clamshell::Environment.setup("tcsh", &block).inspect.should == out
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should provide a $SHELL global variable" do
|
184
|
+
block = proc {
|
185
|
+
if $SHELL == "tcsh"
|
186
|
+
cmd "unlimited coredumpsize"
|
187
|
+
elsif $SHELL == "bash"
|
188
|
+
cmd "ulimit -c unlimited"
|
189
|
+
end
|
190
|
+
}
|
191
|
+
Clamshell::Environment.setup("tcsh", &block).inspect.should == "unlimited coredumpsize"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
######################################
|
2
|
+
# The following will return:
|
3
|
+
# export LC_CTYPE=en_US
|
4
|
+
# export PATH=/usr/bin
|
5
|
+
# export PATH=~/usr/bin:${PATH}
|
6
|
+
# export PATH=~alice/bin:${PATH}
|
7
|
+
# export PATH=~bob/bin:${PATH}
|
8
|
+
# export PATH=~eve/bin:${PATH}
|
9
|
+
# alias EDITOR=vim
|
10
|
+
# echo -e FOO
|
11
|
+
#
|
12
|
+
Environment.setup("bash") do
|
13
|
+
env_var "LC_CTYPE", "en_US"
|
14
|
+
env_var "PATH", "/usr/bin"
|
15
|
+
env_var "PATH", :prepend => "~/usr/bin", :delimiter => ":"
|
16
|
+
|
17
|
+
["~alice/bin", "~bob/bin", "~eve/bin"].each do |p|
|
18
|
+
env_var "PATH", :prepend => p, :delimiter => ":"
|
19
|
+
end
|
20
|
+
|
21
|
+
env_alias "EDITOR", "vim"
|
22
|
+
|
23
|
+
cmd "echo -e FOO"
|
24
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
2
|
+
require 'clamshell'
|
3
|
+
require 'rspec'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
FIXTURES_DIR = File.dirname(__FILE__) + '/fixtures'
|
7
|
+
|
8
|
+
ARGV.clear
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
def capture(stream)
|
12
|
+
begin
|
13
|
+
stream = stream.to_s
|
14
|
+
eval "$#{stream} = StringIO.new"
|
15
|
+
yield
|
16
|
+
result = eval("$#{stream}").string
|
17
|
+
ensure
|
18
|
+
eval("$#{stream} = #{stream.upcase}")
|
19
|
+
end
|
20
|
+
|
21
|
+
result
|
22
|
+
end
|
23
|
+
end
|
data/tasks/rspec.rake
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
begin
|
2
|
+
require 'rake'
|
3
|
+
require 'rspec'
|
4
|
+
rescue LoadError
|
5
|
+
require 'rubygems' unless ENV['NO_RUBYGEMS']
|
6
|
+
require 'rspec'
|
7
|
+
end
|
8
|
+
begin
|
9
|
+
require 'rspec/core/rake_task'
|
10
|
+
rescue LoadError
|
11
|
+
puts <<-EOS
|
12
|
+
To use rspec for testing you must install rspec gem:
|
13
|
+
gem install rspec
|
14
|
+
EOS
|
15
|
+
exit(0)
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "Run all of the spec tests"
|
19
|
+
Rspec::Core::RakeTask.new do |t|
|
20
|
+
t.rspec_opts = ['--options', "spec/spec.opts"]
|
21
|
+
t.pattern = FileList['spec/**/*_spec.rb']
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: clamshell
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 2
|
8
|
+
version: "0.2"
|
9
|
+
platform: ruby
|
10
|
+
authors:
|
11
|
+
- Eric Thomas
|
12
|
+
autorequire:
|
13
|
+
bindir: bin
|
14
|
+
cert_chain: []
|
15
|
+
|
16
|
+
date: 2011-04-02 00:00:00 -04:00
|
17
|
+
default_executable:
|
18
|
+
dependencies:
|
19
|
+
- !ruby/object:Gem::Dependency
|
20
|
+
name: thor
|
21
|
+
prerelease: false
|
22
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
23
|
+
none: false
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :runtime
|
31
|
+
version_requirements: *id001
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: rspec
|
34
|
+
prerelease: false
|
35
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
segments:
|
41
|
+
- 0
|
42
|
+
version: "0"
|
43
|
+
type: :development
|
44
|
+
version_requirements: *id002
|
45
|
+
description: |-
|
46
|
+
Clamshell allows you to setup shell statements which configure
|
47
|
+
your project's environment that doesn't care about which shell
|
48
|
+
(bash, tcsh) the target user will be using.
|
49
|
+
email:
|
50
|
+
- eric.l.m.thomas@gmail.com
|
51
|
+
executables:
|
52
|
+
- clamshell
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files: []
|
56
|
+
|
57
|
+
files:
|
58
|
+
- .gitignore
|
59
|
+
- .rspec
|
60
|
+
- Gemfile
|
61
|
+
- Gemfile.lock
|
62
|
+
- README.md
|
63
|
+
- Rakefile
|
64
|
+
- bin/clamshell
|
65
|
+
- clamshell.gemspec
|
66
|
+
- lib/clamshell.rb
|
67
|
+
- lib/clamshell/cli.rb
|
68
|
+
- lib/clamshell/dsl.rb
|
69
|
+
- lib/clamshell/environment.rb
|
70
|
+
- lib/clamshell/version.rb
|
71
|
+
- spec/clamshell/cli_spec.rb
|
72
|
+
- spec/clamshell/environment_spec.rb
|
73
|
+
- spec/fixtures/Shell.env
|
74
|
+
- spec/spec_helper.rb
|
75
|
+
- tasks/rspec.rake
|
76
|
+
has_rdoc: true
|
77
|
+
homepage: https://github.com/et/clamshell
|
78
|
+
licenses: []
|
79
|
+
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
segments:
|
91
|
+
- 0
|
92
|
+
version: "0"
|
93
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
segments:
|
99
|
+
- 0
|
100
|
+
version: "0"
|
101
|
+
requirements: []
|
102
|
+
|
103
|
+
rubyforge_project: clamshell
|
104
|
+
rubygems_version: 1.3.7
|
105
|
+
signing_key:
|
106
|
+
specification_version: 3
|
107
|
+
summary: Clamshell manages your environment in a shell-independent setup.
|
108
|
+
test_files:
|
109
|
+
- spec/clamshell/cli_spec.rb
|
110
|
+
- spec/clamshell/environment_spec.rb
|
111
|
+
- spec/fixtures/Shell.env
|
112
|
+
- spec/spec_helper.rb
|