sunzi 1.5.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +1 -1
  3. data/CHANGELOG.md +7 -0
  4. data/README.md +14 -37
  5. data/bin/console +14 -0
  6. data/bin/setup +8 -0
  7. data/{bin → exe}/sunzi +0 -0
  8. data/lib/sunzi.rb +18 -22
  9. data/lib/sunzi/actions.rb +35 -0
  10. data/lib/sunzi/cli.rb +6 -139
  11. data/lib/sunzi/command.rb +113 -0
  12. data/lib/sunzi/core_ext.rb +10 -0
  13. data/lib/sunzi/dependency.rb +26 -29
  14. data/lib/sunzi/endpoint.rb +17 -0
  15. data/lib/sunzi/plugin.rb +17 -0
  16. data/sunzi.gemspec +9 -9
  17. data/{lib/templates → templates}/create/.gitignore +0 -0
  18. data/{lib/templates → templates}/create/files/.gitkeep +0 -0
  19. data/{lib/templates → templates}/create/install.sh +13 -7
  20. data/{lib/templates → templates}/create/recipes/sunzi.sh +0 -0
  21. data/{lib/templates → templates}/create/roles/db.sh +0 -0
  22. data/{lib/templates → templates}/create/roles/web.sh +0 -0
  23. data/templates/create/sunzi.yml +27 -0
  24. data/templates/dependency/gemfile.erb +6 -0
  25. data/templates/dependency/install.erb +6 -0
  26. metadata +45 -36
  27. data/lib/sunzi/cloud.rb +0 -24
  28. data/lib/sunzi/cloud/base.rb +0 -98
  29. data/lib/sunzi/cloud/digital_ocean.rb +0 -78
  30. data/lib/sunzi/cloud/linode.rb +0 -154
  31. data/lib/sunzi/dns.rb +0 -25
  32. data/lib/sunzi/dns/base.rb +0 -7
  33. data/lib/sunzi/dns/linode.rb +0 -26
  34. data/lib/sunzi/dns/route53.rb +0 -25
  35. data/lib/sunzi/logger.rb +0 -17
  36. data/lib/sunzi/utility.rb +0 -17
  37. data/lib/templates/create/sunzi.yml +0 -30
  38. data/lib/templates/setup/digital_ocean/digital_ocean.yml +0 -28
  39. data/lib/templates/setup/linode/linode.yml +0 -42
  40. data/test/test_cli.rb +0 -40
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: c79ff341d30ba481b5240cf5c3048195ab083216
4
- data.tar.gz: 3daae0bd5c6856bd049ad389478e7e03defe15bc
2
+ SHA256:
3
+ metadata.gz: 7c118fcfb05d687e587068fea6079f6e365ffbf637ef0a4f73dbd1920dc8f138
4
+ data.tar.gz: 251b57a131bd76db42637bdcd0dad5b373b1a37e964680f5fde2531ff995b768
5
5
  SHA512:
6
- metadata.gz: dbdaa4fe5e8e08d0ef456f798ae66b24023e91ac9a94c1bd3e53e130ce98da248b371d0566322086f4a1c2864d267a9d9368db235c81957fdbc76705d22dde1a
7
- data.tar.gz: 6957a84b58e79c0fedad317489d4d2e010d974962a17a53a6e4b599ff8b94118ed1ff2c015f0755bdc3c7dd292a382218127a30fd421439775dea3e049a0c320
6
+ metadata.gz: 19c134eda8a7b0b53570ad335e79b019daa553ae46cb5f554bc80f2157ac11e10e04e51f0c6444d249d6b97f14aa806a85a7c78a11e555eddccb849fd320666b
7
+ data.tar.gz: be46e99817efb6b0a8e85c5b562297b1b755c3a552ff4977039e70bf672aea636b6fc9cc4e81520120c82f5267e3ff2ba249aed0f061dae9fa4b9adcc48ee1c4
@@ -2,4 +2,4 @@ language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
4
  - 2.0.0
5
- - 2.1.0
5
+ - ruby-head
@@ -1,3 +1,10 @@
1
+ ## 2.0, release 2018-01
2
+ * Plug-in support for commands.
3
+ * Separate "setup" and "teardown" commands for VPS into the "sunzi-vps" gem as a plugin.
4
+ * "attributes" are now "vars" in sunzi.yml and shell scripts.
5
+ * ERB templating (e.g. <%= @vars.environment %>) is always enabled.
6
+ * Individual attribute files compiled/attributes/* are no longer supported. Use ERB template methods instead.
7
+
1
8
  ## 1.5, release 2013-09-27
2
9
  * `sunzi deploy [linode|digital_ocean] [name]` will deploy to the instance with additional attributes.
3
10
 
data/README.md CHANGED
@@ -46,7 +46,7 @@ $ sunzi deploy example.com
46
46
 
47
47
  Now, what it actually does is:
48
48
 
49
- 1. Compile `sunzi.yml` to generate attributes and retrieve remote recipes, then copy files into the `compiled` directory
49
+ 1. Compile `sunzi.yml` to generate variables and retrieve remote recipes, then copy files into the `compiled` directory
50
50
  1. SSH to `example.com` and login as `root`
51
51
  1. Transfer the content of the `compiled` directory to the remote server and extract in `$HOME/sunzi`
52
52
  1. Run `install.sh` on the remote server
@@ -60,12 +60,8 @@ Commands
60
60
 
61
61
  ```bash
62
62
  $ sunzi # Show command help
63
- $ sunzi compile # Compile Sunzi project
64
63
  $ sunzi create # Create a new Sunzi project
65
64
  $ sunzi deploy [user@host:port] [role] [--sudo] # Deploy Sunzi project
66
-
67
- $ sunzi setup [linode|digital_ocean] # Setup a new VM on the cloud services
68
- $ sunzi teardown [linode|digital_ocean] # Teardown an existing VM on the cloud services
69
65
  ```
70
66
 
71
67
  Directory structure
@@ -76,7 +72,7 @@ Here's the directory structure that `sunzi create` automatically generates:
76
72
  ```bash
77
73
  sunzi/
78
74
  install.sh # main script
79
- sunzi.yml # add custom attributes and remote recipes here
75
+ sunzi.yml # add custom variables and remote recipes here
80
76
 
81
77
  recipes/ # put commonly used scripts here, referred from install.sh
82
78
  sunzi.sh
@@ -84,6 +80,7 @@ sunzi/
84
80
  db.sh # to install.sh in the compile phase
85
81
  web.sh
86
82
  files/ # put any files to be transferred
83
+
87
84
  compiled/ # everything under this folder will be transferred to the
88
85
  # remote server (do not edit directly)
89
86
  ```
@@ -91,22 +88,18 @@ sunzi/
91
88
  How do you pass dynamic values?
92
89
  -------------------------------
93
90
 
94
- There are two ways to pass dynamic values to the script - ruby and bash.
95
-
96
- **For ruby (recommended)**: Make sure `eval_erb: true` is set in `sunzi.yml`. In the compile phase, attributes defined in `sunzi.yml` are accessible from any files in the form of `<%= @attributes.ruby_version %>`.
97
-
98
- **For bash**: In the compile phase, attributes defined in `sunzi.yml` are split into multiple files in `compiled/attributes`, one per attribute. Now you can refer to it by `$(cat attributes/ruby_version)` in the script.
91
+ In the compile phase, variables defined in `sunzi.yml` are accessible from any files in the form of `<%= @vars.ruby_version %>`
99
92
 
100
93
  For instance, given the following `install.sh`:
101
94
 
102
95
  ```bash
103
- echo "Goodbye <%= @attributes.goodbye %>, Hello <%= @attributes.hello %>!"
96
+ echo "Goodbye <%= @vars.goodbye %>, Hello <%= @vars.hello %>!"
104
97
  ```
105
98
 
106
99
  With `sunzi.yml`:
107
100
 
108
101
  ```yaml
109
- attributes:
102
+ vars:
110
103
  goodbye: Chef
111
104
  hello: Sunzi
112
105
  ```
@@ -150,19 +143,6 @@ sunzi deploy example.com web
150
143
 
151
144
  It is equivalent to running `install.sh`, followed by `web.sh`.
152
145
 
153
- Cloud Support
154
- -------------
155
-
156
- You can setup a new VM, or teardown an existing VM interactively. Use `sunzi setup` and `sunzi teardown` for that.
157
-
158
- The following screenshot says it all.
159
-
160
- ![Sunzi for Linode](http://farm8.staticflickr.com/7210/6783789868_ab89010d5c.jpg)
161
-
162
- Right now, only [Linode](http://www.linode.com/) and [DigitalOcean](https://www.digitalocean.com) are supported.
163
-
164
- For DNS, Linode and [Amazon Route 53](http://aws.amazon.com/route53/) are supported.
165
-
166
146
  Vagrant
167
147
  -------
168
148
 
@@ -171,21 +151,13 @@ If you're using Sunzi with [Vagrant](http://vagrantup.com/), make sure that you
171
151
  An easy way is to edit `Vagrantfile`:
172
152
 
173
153
  ```ruby
174
- Vagrant::Config.run do |config|
175
- config.vm.provision :shell do |shell|
176
- shell.path = "chpasswd.sh"
154
+ Vagrant.configure("2") do |config|
155
+ config.vm.provision "shell",
156
+ inline: "sudo echo 'root:vagrant' | /usr/sbin/chpasswd"
177
157
  end
178
158
  end
179
159
  ```
180
160
 
181
- with `chpasswd.sh`:
182
-
183
- ```bash
184
- #!/bin/bash
185
-
186
- sudo echo 'root:vagrant' | /usr/sbin/chpasswd
187
- ```
188
-
189
161
  and now run `vagrant up`, it will change the root password to `vagrant`.
190
162
 
191
163
  Also keep in mind that you need to specify the port number 2222.
@@ -193,3 +165,8 @@ Also keep in mind that you need to specify the port number 2222.
193
165
  ```bash
194
166
  $ sunzi deploy localhost:2222
195
167
  ```
168
+
169
+ Demonstration Videos
170
+ -------
171
+
172
+ You can watch video on how to deploy a Rails 4.1 app with Sunzi and Capistrano 3 at http://youtu.be/3mwupXqtkmg
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'sunzi'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require 'pry'
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
File without changes
@@ -1,28 +1,24 @@
1
- require 'thor'
2
- require 'rainbow'
1
+ # Stdlib
2
+ require 'erb'
3
+ require 'pathname'
3
4
  require 'yaml'
4
5
 
5
- # Starting 2.0.0, Rainbow no longer patches string with the color method by default.
6
- require 'rainbow/version'
7
- require 'rainbow/ext/string' unless Rainbow::VERSION < '2.0.0'
6
+ # Gems
7
+ require 'hashugar'
8
+ require 'rainbow/ext/string'
9
+ require 'thor'
8
10
 
11
+ # Sunzi
9
12
  module Sunzi
10
- autoload :Cli, 'sunzi/cli'
11
- autoload :Cloud, 'sunzi/cloud'
12
- autoload :Dependency, 'sunzi/dependency'
13
- autoload :DNS, 'sunzi/dns'
14
- autoload :Logger, 'sunzi/logger'
15
- autoload :Utility, 'sunzi/utility'
13
+ GemRoot = Pathname.new(Gem.loaded_specs['sunzi'].gem_dir)
14
+ end
16
15
 
17
- class Cloud
18
- autoload :Base, 'sunzi/cloud/base'
19
- autoload :Linode, 'sunzi/cloud/linode'
20
- autoload :DigitalOcean, 'sunzi/cloud/digital_ocean'
21
- end
16
+ require 'sunzi/core_ext'
17
+ require 'sunzi/actions'
18
+ require 'sunzi/dependency'
22
19
 
23
- class DNS
24
- autoload :Base, 'sunzi/dns/base'
25
- autoload :Linode, 'sunzi/dns/linode'
26
- autoload :Route53, 'sunzi/dns/route53'
27
- end
28
- end
20
+ require 'sunzi/cli'
21
+
22
+ # Plug-ins
23
+ require 'sunzi/plugin'
24
+ Sunzi::Plugin.load
@@ -0,0 +1,35 @@
1
+ require 'forwardable'
2
+
3
+ module Sunzi
4
+ class Actions < Thor
5
+ # This class exists because thor has to be inherited AND included to import actions.
6
+ #
7
+ # https://github.com/erikhuda/thor/wiki/Actions
8
+ #
9
+ # This interface is ugly. Instead, initialize once and reuse everywhere.
10
+
11
+ include Thor::Actions
12
+
13
+ source_root GemRoot
14
+
15
+ # include this module to use delegate_to_thor method.
16
+ module Delegate
17
+ def self.included(base)
18
+ base.extend Forwardable
19
+ base.extend ClassMethods
20
+ end
21
+
22
+ module ClassMethods
23
+ def delegate_to_thor(*args)
24
+ def_delegators :'Sunzi.thor', *args
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ class << self
31
+ def thor
32
+ @thor ||= Sunzi::Actions.new
33
+ end
34
+ end
35
+ end
@@ -1,35 +1,22 @@
1
- require 'open3'
2
- require 'ostruct'
3
- require 'net/ssh'
1
+ require 'sunzi/command'
4
2
 
5
3
  module Sunzi
6
4
  class Cli < Thor
7
- include Thor::Actions
8
5
 
9
6
  desc 'create', 'Create sunzi project'
10
7
  def create(project = 'sunzi')
11
- do_create(project)
8
+ Sunzi::Command.new.create(project)
12
9
  end
13
10
 
14
- desc 'deploy [user@host:port] [role] [--sudo] or deploy [linode|digital_ocean] [name] [role] [--sudo]', 'Deploy sunzi project'
15
- method_options :sudo => false
11
+ desc 'deploy [user@host:port] [role] [--sudo]', 'Deploy sunzi project'
12
+ method_options sudo: false
16
13
  def deploy(first, *args)
17
- do_deploy(first, *args)
14
+ Sunzi::Command.new.deploy(first, *args)
18
15
  end
19
16
 
20
17
  desc 'compile', 'Compile sunzi project'
21
18
  def compile(role = nil)
22
- do_compile(role)
23
- end
24
-
25
- desc 'setup [linode|digital_ocean]', 'Setup a new VM'
26
- def setup(provider)
27
- Sunzi::Cloud.new(self, provider).setup
28
- end
29
-
30
- desc 'teardown [linode|digital_ocean]', 'Teardown an existing VM'
31
- def teardown(provider)
32
- Sunzi::Cloud.new(self, provider).teardown
19
+ Sunzi::Command.new.compile(role)
33
20
  end
34
21
 
35
22
  desc 'version', 'Show version'
@@ -37,125 +24,5 @@ module Sunzi
37
24
  puts Gem.loaded_specs['sunzi'].version.to_s
38
25
  end
39
26
 
40
- no_tasks do
41
- include Sunzi::Utility
42
-
43
- def self.source_root
44
- File.expand_path('../../',__FILE__)
45
- end
46
-
47
- def do_create(project)
48
- copy_file 'templates/create/.gitignore', "#{project}/.gitignore"
49
- copy_file 'templates/create/sunzi.yml', "#{project}/sunzi.yml"
50
- copy_file 'templates/create/install.sh', "#{project}/install.sh"
51
- copy_file 'templates/create/recipes/sunzi.sh', "#{project}/recipes/sunzi.sh"
52
- copy_file 'templates/create/roles/db.sh', "#{project}/roles/db.sh"
53
- copy_file 'templates/create/roles/web.sh', "#{project}/roles/web.sh"
54
- copy_file 'templates/create/files/.gitkeep', "#{project}/files/.gitkeep"
55
- end
56
-
57
- def do_deploy(first, *args)
58
- if ['linode', 'digital_ocean'].include?(first)
59
- @instance_attributes = YAML.load(File.read("#{first}/instances/#{args[0]}.yml"))
60
- target = @instance_attributes[:fqdn]
61
- role = args[1]
62
- else
63
- target = first
64
- role = args[0]
65
- end
66
-
67
- sudo = 'sudo ' if options.sudo?
68
- user, host, port = parse_target(target)
69
- endpoint = "#{user}@#{host}"
70
-
71
- # compile attributes and recipes
72
- do_compile(role)
73
-
74
- # The host key might change when we instantiate a new VM, so
75
- # we remove (-R) the old host key from known_hosts.
76
- `ssh-keygen -R #{host} 2> /dev/null`
77
-
78
- remote_commands = <<-EOS
79
- rm -rf ~/sunzi &&
80
- mkdir ~/sunzi &&
81
- cd ~/sunzi &&
82
- tar xz &&
83
- #{sudo}bash install.sh
84
- EOS
85
-
86
- remote_commands.strip! << ' && rm -rf ~/sunzi' if @config['preferences'] and @config['preferences']['erase_remote_folder']
87
-
88
- local_commands = <<-EOS
89
- cd compiled
90
- tar cz . | ssh -o 'StrictHostKeyChecking no' #{endpoint} -p #{port} '#{remote_commands}'
91
- EOS
92
-
93
- Open3.popen3(local_commands) do |stdin, stdout, stderr|
94
- stdin.close
95
- t = Thread.new do
96
- while (line = stderr.gets)
97
- print line.color(:red)
98
- end
99
- end
100
- while (line = stdout.gets)
101
- print line.color(:green)
102
- end
103
- t.join
104
- end
105
- end
106
-
107
- def do_compile(role)
108
- # Check if you're in the sunzi directory
109
- abort_with 'You must be in the sunzi folder' unless File.exists?('sunzi.yml')
110
- # Check if role exists
111
- abort_with "#{role} doesn't exist!" if role and !File.exists?("roles/#{role}.sh")
112
-
113
- # Load sunzi.yml
114
- @config = YAML.load(File.read('sunzi.yml'))
115
-
116
- # Merge instance attributes
117
- @config['attributes'] ||= {}
118
- @config['attributes'].update(Hash[@instance_attributes.map{|k,v| [k.to_s, v] }]) if @instance_attributes
119
-
120
- # Break down attributes into individual files
121
- (@config['attributes'] || {}).each {|key, value| create_file "compiled/attributes/#{key}", value }
122
-
123
- # Retrieve remote recipes via HTTP
124
- cache_remote_recipes = @config['preferences'] && @config['preferences']['cache_remote_recipes']
125
- (@config['recipes'] || []).each do |key, value|
126
- next if cache_remote_recipes and File.exists?("compiled/recipes/#{key}.sh")
127
- get value, "compiled/recipes/#{key}.sh"
128
- end
129
-
130
- # Copy local files
131
- @attributes = OpenStruct.new(@config['attributes'])
132
- copy_or_template = (@config['preferences'] && @config['preferences']['eval_erb']) ? :template : :copy_file
133
- Dir['recipes/*'].each {|file| send copy_or_template, File.expand_path(file), "compiled/recipes/#{File.basename(file)}" }
134
- Dir['roles/*'].each {|file| send copy_or_template, File.expand_path(file), "compiled/roles/#{File.basename(file)}" }
135
- Dir['files/*'].each {|file| send copy_or_template, File.expand_path(file), "compiled/files/#{File.basename(file)}" }
136
- (@config['files'] || []).each {|file| send copy_or_template, File.expand_path(file), "compiled/files/#{File.basename(file)}" }
137
-
138
- # Build install.sh
139
- if role
140
- if copy_or_template == :template
141
- template File.expand_path('install.sh'), 'compiled/_install.sh'
142
- create_file 'compiled/install.sh', File.binread('compiled/_install.sh') << "\n" << File.binread("compiled/roles/#{role}.sh")
143
- else
144
- create_file 'compiled/install.sh', File.binread('install.sh') << "\n" << File.binread("roles/#{role}.sh")
145
- end
146
- else
147
- send copy_or_template, File.expand_path('install.sh'), 'compiled/install.sh'
148
- end
149
- end
150
-
151
- def parse_target(target)
152
- target.match(/(.*@)?(.*?)(:.*)?$/)
153
- # Load ssh config if it exists
154
- config = Net::SSH::Config.for($2)
155
- [ ($1 && $1.delete('@') || config[:user] || 'root'),
156
- config[:host_name] || $2,
157
- ($3 && $3.delete(':') || config[:port] && config[:port].to_s || '22') ]
158
- end
159
- end
160
27
  end
161
28
  end
@@ -0,0 +1,113 @@
1
+ require 'open3'
2
+ require 'sunzi/endpoint'
3
+
4
+ module Sunzi
5
+ class Command
6
+ include Sunzi::Actions::Delegate
7
+
8
+ delegate_to_thor :copy_file, :template, :get, :append_to_file, :options
9
+
10
+ def create(project)
11
+ copy_file 'templates/create/.gitignore', "#{project}/.gitignore"
12
+ copy_file 'templates/create/sunzi.yml', "#{project}/sunzi.yml"
13
+ copy_file 'templates/create/install.sh', "#{project}/install.sh"
14
+ copy_file 'templates/create/recipes/sunzi.sh', "#{project}/recipes/sunzi.sh"
15
+ copy_file 'templates/create/roles/db.sh', "#{project}/roles/db.sh"
16
+ copy_file 'templates/create/roles/web.sh', "#{project}/roles/web.sh"
17
+ copy_file 'templates/create/files/.gitkeep', "#{project}/files/.gitkeep"
18
+ end
19
+
20
+ def deploy(first, *args)
21
+ role = args[0]
22
+
23
+ sudo = 'sudo ' if options.sudo?
24
+ endpoint = Endpoint.new(first)
25
+
26
+ # compile vars and recipes
27
+ compile(role)
28
+
29
+ # The host key might change when we instantiate a new VM, so
30
+ # we remove (-R) the old host key from known_hosts.
31
+ `ssh-keygen -R #{endpoint.host} 2> /dev/null`
32
+
33
+ remote_commands = <<-EOS
34
+ rm -rf ~/sunzi &&
35
+ mkdir ~/sunzi &&
36
+ cd ~/sunzi &&
37
+ tar xz &&
38
+ #{sudo}bash install.sh
39
+ EOS
40
+
41
+ remote_commands.strip! << ' && rm -rf ~/sunzi' if config.preferences.erase_remote_folder
42
+
43
+ local_commands = <<-EOS
44
+ cd compiled
45
+ tar cz . | ssh -o 'StrictHostKeyChecking no' #{endpoint.user}@#{endpoint.host} -p #{endpoint.port} '#{remote_commands}'
46
+ EOS
47
+
48
+ Open3.popen3(local_commands) do |stdin, stdout, stderr|
49
+ stdin.close
50
+ t = Thread.new do
51
+ while (line = stderr.gets)
52
+ print line.color(:red)
53
+ end
54
+ end
55
+ while (line = stdout.gets)
56
+ print line.color(:green)
57
+ end
58
+ t.join
59
+ end
60
+ end
61
+
62
+ def compile(role = nil)
63
+ abort_with "#{role} doesn't exist!" if role && !File.exist?("roles/#{role}.sh")
64
+ abort_with 'As of v2, "attributes" are now "vars" in sunzi.yml and shell scripts.' if config.attributes
65
+
66
+ # Retrieve remote recipes via HTTP
67
+ (config.recipes || []).each do |key, value|
68
+ dest = "compiled/recipes/#{key}.sh"
69
+ next if config.preferences.cache_remote_recipes && File.exist?(dest)
70
+ get value, dest
71
+ end
72
+
73
+ @vars = config.vars # Used within ERB templates
74
+
75
+ # Copy local files to compiled folder
76
+ files = Dir['{recipes,roles,files}/**/*'].select { |file| File.file?(file) }
77
+
78
+ files.each do |file|
79
+ render file, "compiled/#{file}"
80
+ end
81
+
82
+ # Copy files specified in sunzi.yml
83
+ (config.files || []).each do |file|
84
+ render file, "compiled/files/#{File.basename(file)}"
85
+ end
86
+
87
+ # Build install.sh
88
+ render 'install.sh', 'compiled/install.sh'
89
+
90
+ # Append role at the bottom of install.sh
91
+ if role
92
+ append_to_file 'compiled/install.sh', "\n" + File.read("compiled/roles/#{role}.sh")
93
+ end
94
+ end
95
+
96
+ private
97
+
98
+ def config
99
+ @config ||= begin
100
+ abort_with 'You must be in a sunzi folder' unless File.exist?('sunzi.yml')
101
+
102
+ YAML.load(File.read('sunzi.yml')).to_hashugar
103
+ end
104
+ end
105
+
106
+ # template method requires absolute path to work with current directory
107
+ #
108
+ def render(source, target)
109
+ template File.expand_path(source), target, context: binding
110
+ end
111
+
112
+ end
113
+ end