linecook 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/History +60 -0
  2. data/License.txt +22 -0
  3. data/README +98 -0
  4. data/bin/linecook +58 -0
  5. data/cookbook +0 -0
  6. data/lib/linecook/attributes.rb +22 -0
  7. data/lib/linecook/commands/command.rb +48 -0
  8. data/lib/linecook/commands/command_error.rb +6 -0
  9. data/lib/linecook/commands/env.rb +23 -0
  10. data/lib/linecook/commands/helper.rb +51 -0
  11. data/lib/linecook/commands/helpers.rb +28 -0
  12. data/lib/linecook/commands/init.rb +82 -0
  13. data/lib/linecook/commands/package.rb +39 -0
  14. data/lib/linecook/commands/vbox.rb +85 -0
  15. data/lib/linecook/commands.rb +6 -0
  16. data/lib/linecook/cookbook.rb +104 -0
  17. data/lib/linecook/helper.rb +117 -0
  18. data/lib/linecook/package.rb +197 -0
  19. data/lib/linecook/recipe.rb +103 -0
  20. data/lib/linecook/shell/posix.rb +145 -0
  21. data/lib/linecook/shell/test.rb +254 -0
  22. data/lib/linecook/shell/unix.rb +117 -0
  23. data/lib/linecook/shell/utils.rb +138 -0
  24. data/lib/linecook/shell.rb +11 -0
  25. data/lib/linecook/template.rb +111 -0
  26. data/lib/linecook/test/file_test.rb +77 -0
  27. data/lib/linecook/test/regexp_escape.rb +86 -0
  28. data/lib/linecook/test.rb +172 -0
  29. data/lib/linecook/utils.rb +53 -0
  30. data/lib/linecook/version.rb +8 -0
  31. data/lib/linecook.rb +6 -0
  32. data/templates/Gemfile +2 -0
  33. data/templates/README +90 -0
  34. data/templates/Rakefile +149 -0
  35. data/templates/_gitignore +5 -0
  36. data/templates/attributes/project_name.rb +4 -0
  37. data/templates/cookbook +9 -0
  38. data/templates/files/file.txt +1 -0
  39. data/templates/helpers/project_name/echo.erb +5 -0
  40. data/templates/project_name.gemspec +30 -0
  41. data/templates/recipes/project_name.rb +20 -0
  42. data/templates/scripts/project_name.yml +7 -0
  43. data/templates/templates/template.txt.erb +3 -0
  44. data/templates/vbox/setup/virtual_box +86 -0
  45. data/templates/vbox/ssh/id_rsa +27 -0
  46. data/templates/vbox/ssh/id_rsa.pub +1 -0
  47. metadata +166 -0
data/History ADDED
@@ -0,0 +1,60 @@
1
+ == 0.6.2 2011/01/06
2
+
3
+ * added helper command to build a single helper
4
+ * improved rake tasks in template to build as-needed
5
+ * bugfix in Test#assert_content
6
+
7
+ == 0.6.1 2011/01/06
8
+
9
+ * added version flag to linecook
10
+ * updated/improved help
11
+
12
+ == 0.6.0 2011/01/06
13
+
14
+ Rework with a cleaner interface. Now package interfaces with env state, which
15
+ gets passed around, rather than the hodge-podge of state passed around
16
+ previously.
17
+
18
+ == 0.5.0 2010/12/28
19
+
20
+ * rework with the ability to test more
21
+
22
+ == 0.4.0 2010/12/27
23
+
24
+ * enhanced linecook executable with commands
25
+ * added sketches of default shell helpers
26
+ * bug fixes
27
+
28
+ == 0.3.1 2010/12/23
29
+
30
+ * prevent capture_path from stripping content
31
+ * added test tasks to template
32
+
33
+ == 0.3.0 2010/12/23
34
+
35
+ * improved configuration of cookbook manifest
36
+ * helpers are now created in lib directory, by
37
+ default under the Linebook namespace
38
+ * added bundler support to template
39
+ * renamed LineCook as Linecook
40
+
41
+ == 0.2.1 2010/12/21
42
+
43
+ * added missing line_cook executable
44
+
45
+ == 0.2.0 2010/12/20
46
+
47
+ Complete overhaul.
48
+
49
+ * reworked script generation using rake
50
+ * sketched out functionality for use of gems
51
+ * added line_cook executable to generate scaffold
52
+
53
+ == 0.1.1 2010/12/07
54
+
55
+ * added nodoc to definition templates
56
+ * added tap.yml for generator discover
57
+
58
+ == 0.1.0 2010/12/07
59
+
60
+ Initial release.
data/License.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2010 Pinnacol Assurance
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software. Except as contained in this
12
+ notice, the name(s) of the above copyright holders shall not be used in
13
+ advertising or otherwise to promote the sale, use or other dealings in this
14
+ Software without prior written authorization.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
data/README ADDED
@@ -0,0 +1,98 @@
1
+ = Linecook
2
+
3
+ A shell script generator.
4
+
5
+ == Description
6
+
7
+ Linecook lets you generate shell scripts using an extensible set of tiny ERB
8
+ helpers. The helpers output a shell script that you can use to (for example)
9
+ provision a server.
10
+
11
+ The workflow is like this:
12
+
13
+ * define helpers
14
+ * write a recipe using the helpers
15
+ * generate a script from the recipe
16
+
17
+ Linecook provides very basic helpers for POSIX shells and a command-line tool
18
+ for generating scripts and running them on a test vm.
19
+
20
+ == Usage
21
+
22
+ Generate a cookbook.
23
+
24
+ % linecook init example
25
+ % cd example
26
+
27
+ Make some helpers.
28
+
29
+ [helpers/example/_head.rb]
30
+ COLOR_CODES = Hash[*%W{
31
+ black 0;30 red 0;31
32
+ white 1;37 green 0;32
33
+ light_gray 0;37 blue 0;34
34
+ }]
35
+
36
+ [helpers/example/color.erb]
37
+ Adds color to a string.
38
+ (color, str)
39
+ --
40
+ \033[<%= COLOR_CODES[color.to_s] %>m<%= str %>\033[0m
41
+
42
+ [helpers/example/echo.erb]
43
+ Echo a string in color
44
+ (str, options={})
45
+ color = options[:color]
46
+ --
47
+ echo -e '<%= color ? _color(color, str) : str %>'
48
+
49
+ Define default attributes.
50
+
51
+ [recipes/example.rb]
52
+ attrs['example']['n'] = 3
53
+ attrs['example']['color'] = 'blue'
54
+
55
+ Use them in a recipe.
56
+
57
+ [recipes/example.rb]
58
+ helpers "example"
59
+ attributes "example"
60
+
61
+ attrs['example']['n'].times do
62
+ echo "I will not manually configure my server", :color => attrs['example']['color']
63
+ end
64
+
65
+ Define a script using the recipe.
66
+
67
+ [scripts/example.yml]
68
+ linecook:
69
+ recipe_name: example
70
+ script_name: example.sh
71
+ example:
72
+ n: 100
73
+
74
+ Generate a script from the helpers, attributes, and recipe.
75
+
76
+ % rake scripts
77
+
78
+ Start the VM and run the scripts (see the docs for setting up a VM).
79
+
80
+ % rake vbox:start
81
+ % rake vbox:ssh
82
+ vm: bash /vbox/scripts/example.sh
83
+ vm: exit
84
+
85
+ Stop the VM.
86
+
87
+ % rake vbox:stop
88
+
89
+ == Installation
90
+
91
+ Linecook is available as a gem on {Gemcutter}[http://gemcutter.org/gems/linecook]
92
+
93
+ % gem install linecook
94
+
95
+ == Info
96
+
97
+ Developer:: {Simon Chiang}[http://bahuvrihi.wordpress.com]
98
+ License:: {MIT-Style}[link:files/License_txt.html]
data/bin/linecook ADDED
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'linecook/version'
4
+ require 'linecook/commands'
5
+ registry = Linecook::Commands::Command.registry
6
+
7
+ cmd = ARGV.shift
8
+ cmd_class = registry[cmd]
9
+
10
+ if cmd_class.nil?
11
+ case cmd
12
+ when nil, '-h', '--help'
13
+ puts "usage: linecook [options] COMMAND"
14
+ puts
15
+ puts "commands: "
16
+ registry.keys.sort.each do |key|
17
+ cmd_class = registry[key]
18
+ puts " %-16s %s" % [key, cmd_class.desc]
19
+ end
20
+ puts
21
+ puts "options:"
22
+ puts " -%s, --%-10s %s" % ['v', 'version', 'print version']
23
+ puts " -%s, --%-10s %s" % ['h', 'help', 'print this help']
24
+
25
+ exit
26
+ when '-v', '--version'
27
+ puts "version #{Linecook::VERSION} -- #{Linecook::WEBSITE}"
28
+ exit
29
+ else
30
+ puts "unknown command: #{cmd.inspect}"
31
+ exit 1
32
+ end
33
+ end
34
+
35
+ parser = ConfigParser.new
36
+ parser.add cmd_class.configurations
37
+ parser.on '-h', '--help', 'print this help' do
38
+ puts "usage: linecook [options] #{cmd} #{cmd_class.args}"
39
+
40
+ desc = cmd_class.desc.wrap
41
+ unless desc.empty?
42
+ puts
43
+ puts cmd_class.desc.wrap
44
+ puts
45
+ end
46
+
47
+ puts "options:"
48
+ puts parser.to_s
49
+ exit
50
+ end
51
+ parser.parse! ARGV
52
+
53
+ begin
54
+ cmd_class.new(parser.config).call ARGV
55
+ rescue Linecook::Commands::CommandError
56
+ puts $!.message
57
+ exit 1
58
+ end
data/cookbook ADDED
File without changes
@@ -0,0 +1,22 @@
1
+ require 'linecook/utils'
2
+
3
+ module Linecook
4
+ class Attributes
5
+ attr_reader :attrs
6
+ attr_reader :context
7
+
8
+ def initialize(context={})
9
+ @context = context
10
+ reset(true)
11
+ end
12
+
13
+ def current
14
+ @current ||= Utils.serial_merge(attrs, context)
15
+ end
16
+
17
+ def reset(full=true)
18
+ @attrs = Utils.nest_hash if full
19
+ @current = nil
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,48 @@
1
+ require 'linecook/commands/command_error'
2
+ require 'configurable'
3
+
4
+ module Linecook
5
+ module Commands
6
+ class Command
7
+ class << self
8
+ def registry
9
+ REGISTRY
10
+ end
11
+
12
+ def inherited(base)
13
+ super
14
+ registry[base.to_s.split('::').last.downcase] = base
15
+ end
16
+ end
17
+
18
+ REGISTRY = {}
19
+
20
+ extend Lazydoc::Attributes
21
+ include Configurable
22
+
23
+ lazy_attr :desc
24
+ lazy_attr :args, :process
25
+ lazy_register :process, Lazydoc::Arguments
26
+
27
+ def initialize(config)
28
+ initialize_config(config)
29
+ end
30
+
31
+ def log(action, msg)
32
+ puts(" %s %s" % [action, msg])
33
+ end
34
+
35
+ def sh(cmd)
36
+ system cmd
37
+ end
38
+
39
+ def call(argv)
40
+ process(*argv)
41
+ end
42
+
43
+ def process(*args)
44
+ raise NotImplementedError
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,6 @@
1
+ module Linecook
2
+ module Commands
3
+ class CommandError < RuntimeError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,23 @@
1
+ require 'linecook/commands/command'
2
+ require 'linecook/cookbook'
3
+
4
+ module Linecook
5
+ module Commands
6
+
7
+ # ::desc prints the cookbook env
8
+ #
9
+ # Print the cookbook env.
10
+ #
11
+ class Env < Command
12
+ config :cookbook_dir, '.', :short => :d # the cookbook directory
13
+ config :path, nil # package path
14
+
15
+ def process(*keys)
16
+ current = Linecook::Cookbook.init(cookbook_dir).env(path)
17
+ keys.each {|key| current = current[key] if current }
18
+
19
+ YAML.dump(current, $stdout)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,51 @@
1
+ require 'linecook/commands/command'
2
+ require 'linecook/helper'
3
+ require 'linecook/utils'
4
+
5
+ module Linecook
6
+ module Commands
7
+
8
+ # ::desc generates a helper
9
+ class Helper < Command
10
+ config :cookbook_dir, '.', :short => :d # the cookbook directory
11
+ config :namespace, 'linebook', :short => :n # the helper namespace
12
+ config :force, false, :short => :f, &c.flag # force creation
13
+
14
+ include Utils
15
+
16
+ def process(name, *sources)
17
+ name = underscore(name)
18
+
19
+ const_path = namespace ? File.join(namespace, name) : name
20
+ const_name = camelize(const_path)
21
+
22
+ sources = default_sources(name) if sources.empty?
23
+ target = File.expand_path(File.join('lib', "#{const_path}.rb"), cookbook_dir)
24
+
25
+ if sources.empty?
26
+ raise CommandError, "no sources specified (and none could be found)"
27
+ end
28
+
29
+ if File.exists?(target) && !force
30
+ raise CommandError, "already exists: #{target}"
31
+ end
32
+
33
+ log :create, const_name
34
+
35
+ helper = Linecook::Helper.new(const_name, sources)
36
+ content = helper.build
37
+
38
+ target_dir = File.dirname(target)
39
+ unless File.exists?(target_dir)
40
+ FileUtils.mkdir_p(target_dir)
41
+ end
42
+
43
+ File.open(target, 'w') {|io| io << content }
44
+ end
45
+
46
+ def default_sources(name)
47
+ Dir.glob File.join(cookbook_dir, 'helpers', name, '**/*')
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,28 @@
1
+ require 'linecook/commands/helper'
2
+
3
+ module Linecook
4
+ module Commands
5
+
6
+ # ::desc generates all helpers
7
+ #
8
+ # Generates helpers that match the input patterns (by default all,
9
+ # helpers).
10
+ #
11
+ class Helpers < Helper
12
+ def process
13
+ helpers_dir = File.expand_path('helpers', cookbook_dir)
14
+
15
+ sources = {}
16
+ Dir.glob("#{helpers_dir}/**/*").each do |source|
17
+ next if File.directory?(source)
18
+ (sources[File.dirname(source)] ||= []) << source
19
+ end
20
+
21
+ sources.each_pair do |dir, sources|
22
+ name = dir[(helpers_dir.length+1)..-1]
23
+ super(name, *sources)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,82 @@
1
+ require 'linecook/commands/command'
2
+ require 'fileutils'
3
+ require 'erb'
4
+ require 'ostruct'
5
+
6
+ module Linecook
7
+ module Commands
8
+
9
+ # ::desc init a linecook scaffold
10
+ #
11
+ # Initializes a linecook scaffold in the specified directory. This
12
+ # initializer is currently very basic; it is not a true generator.
13
+ #
14
+ class Init < Command
15
+ config :force, false, :short => :f, &c.flag # force creation
16
+
17
+ def source_dir
18
+ @source_dir ||= File.expand_path('../../../../templates', __FILE__)
19
+ end
20
+
21
+ def process(project_dir)
22
+ project_dir = File.expand_path(project_dir)
23
+
24
+ prepare project_dir
25
+ template project_dir
26
+ end
27
+
28
+ def prepare(project_dir)
29
+ if File.exists?(project_dir)
30
+ unless force
31
+ raise CommandError.new("already exists: #{project_dir}")
32
+ end
33
+
34
+ current_dir = File.expand_path('.')
35
+ unless project_dir.index(current_dir) == 0 && project_dir.length > current_dir.length
36
+ raise CommandError.new("cannot force creation of current or parent directory (safety issue)")
37
+ end
38
+
39
+ FileUtils.rm_rf(project_dir)
40
+ end
41
+ end
42
+
43
+ def template(project_dir, project_name=nil)
44
+ project_name ||= File.basename(project_dir)
45
+ context = OpenStruct.new(:project_name => project_name).send(:binding)
46
+
47
+ #
48
+ # Copy template files into place
49
+ #
50
+
51
+ Dir.glob("#{source_dir}/**/*").each do |source|
52
+ if File.directory?(source)
53
+ next
54
+ end
55
+
56
+ path = source[(source_dir.length + 1)..-1]
57
+ path = path.sub('project_name', project_name).sub(/^_/, '.')
58
+ target = File.join(project_dir, path)
59
+
60
+ log :create, path
61
+
62
+ target_dir = File.dirname(target)
63
+ unless File.exists?(target_dir)
64
+ FileUtils.mkdir_p(target_dir)
65
+ end
66
+
67
+ File.open(target, 'w') do |io|
68
+ erb = ERB.new(File.read(source), nil, '<>')
69
+ erb.filename = source
70
+ io << erb.result(context)
71
+ end
72
+ end
73
+
74
+ # Link up scripts into vbox
75
+ source = File.join(project_dir, 'scripts')
76
+ target = File.join(project_dir, 'vbox/scripts')
77
+ FileUtils.ln_s source, target
78
+ end
79
+ end
80
+ end
81
+ end
82
+
@@ -0,0 +1,39 @@
1
+ require 'linecook/commands/command'
2
+ require 'linecook/cookbook'
3
+ require 'linecook/recipe'
4
+ require 'yaml'
5
+
6
+ module Linecook
7
+ module Commands
8
+
9
+ # ::desc generates a package
10
+ #
11
+ # Generates a package.
12
+ #
13
+ class Package < Command
14
+ config :cookbook_dir, '.', :short => :d # the cookbook directory
15
+ config :force, false, :short => :f, &c.flag # force creation
16
+
17
+ def process(source, target=nil)
18
+ target ||= default_target(source)
19
+
20
+ if File.exists?(target)
21
+ if force
22
+ FileUtils.rm_r(target)
23
+ else
24
+ raise "already exists: #{target}"
25
+ end
26
+ end
27
+
28
+ log :create, File.basename(target)
29
+
30
+ env = Linecook::Cookbook.init(cookbook_dir).env(source)
31
+ Linecook::Recipe.build(env).export(target)
32
+ end
33
+
34
+ def default_target(source)
35
+ source.chomp(File.extname(source))
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,85 @@
1
+ module Linecook
2
+ module Commands
3
+
4
+ # Linecook::Commands::Start::desc start the vm
5
+ class Start < Command
6
+ config :type, 'headless'
7
+
8
+ def process(vmname='vbox')
9
+ unless `VBoxManage -q list runningvms`.include?(VMNAME)
10
+ sh "VBoxManage -q startvm #{vmname} --type #{type}"
11
+ end
12
+ end
13
+ end
14
+
15
+ # Linecook::Commands::Stop::desc stop the vm
16
+ class Stop < Command
17
+ def process(vmname='vbox')
18
+ if `VBoxManage -q list runningvms`.include?(vmname)
19
+ sh "VBoxManage -q controlvm #{vmname} poweroff"
20
+ end
21
+ end
22
+ end
23
+
24
+ # Linecook::Commands::Reset::desc reset vm to a snapshot
25
+ class Reset < Command
26
+
27
+ config :type, 'headless'
28
+ config :snapshot, 'BASE'
29
+
30
+ def process(vmname='vbox')
31
+ if `VBoxManage -q list runningvms`.include?(vmname)
32
+ sh "VBoxManage -q controlvm #{vmname} poweroff"
33
+ end
34
+
35
+ sh "VBoxManage -q snapshot #{vmname} restore #{snapshot.upcase}"
36
+ sh "VBoxManage -q startvm #{vmname} --type #{type}"
37
+ end
38
+ end
39
+
40
+ # Linecook::Commands::Snapshot::desc take a snapshop
41
+ class Snapshot < Command
42
+ def process(snapshot, vmname='vbox')
43
+ `VBoxManage -q snapshot #{vmname} delete #{snapshot.upcase} > /dev/null`
44
+ sh "VBoxManage -q snapshot #{vmname} take #{snapshot.upcase}"
45
+ end
46
+ end
47
+
48
+ # Linecook::Commands::State::desc print the vm state
49
+ class State < Command
50
+ def process(vmname='vbox')
51
+ if `VBoxManage -q list runningvms`.include?(vmname)
52
+ puts "running"
53
+ else
54
+ puts "stopped"
55
+ end
56
+ end
57
+ end
58
+
59
+ # Linecook::Commands::Ssh::desc ssh to vm
60
+ class Ssh < Command
61
+
62
+ config :port, 2222, &c.integer
63
+ config :user, 'vbox'
64
+ config :keypath, File.expand_path('../../../../vbox/ssh/id_rsa', __FILE__)
65
+
66
+ def process(cmd=nil)
67
+ # To prevent ssh errors, protect the private key
68
+ FileUtils.chmod(0600, keypath)
69
+
70
+ # Patterned after vagrant/ssh.rb (circa 0.6.6)
71
+ platform = RUBY_PLATFORM.to_s.downcase
72
+ ssh = "ssh -p #{port} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o IdentitiesOnly=yes -i #{keypath} #{user}@localhost #{cmd}"
73
+
74
+ # Some hackery going on here. On Mac OS X Leopard (10.5), exec fails
75
+ # (GH-51). As a workaround, we fork and wait. On all other platforms, we
76
+ # simply exec.
77
+
78
+ pid = nil
79
+ pid = fork if platform.include?("darwin9") || platform.include?("darwin8")
80
+ Kernel.exec(ssh) if pid.nil?
81
+ Process.wait(pid) if pid
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,6 @@
1
+ require 'linecook/commands/init'
2
+ require 'linecook/commands/helper'
3
+ require 'linecook/commands/helpers'
4
+ require 'linecook/commands/package'
5
+ require 'linecook/commands/env'
6
+ require 'linecook/commands/vbox'