fpm-fry 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/bin/fpm-fry +10 -0
  3. data/lib/cabin/nice_output.rb +70 -0
  4. data/lib/fpm/fry/block_enumerator.rb +25 -0
  5. data/lib/fpm/fry/build_output_parser.rb +22 -0
  6. data/lib/fpm/fry/client.rb +162 -0
  7. data/lib/fpm/fry/command/cook.rb +370 -0
  8. data/lib/fpm/fry/command.rb +90 -0
  9. data/lib/fpm/fry/detector.rb +109 -0
  10. data/lib/fpm/fry/docker_file.rb +149 -0
  11. data/lib/fpm/fry/joined_io.rb +63 -0
  12. data/lib/fpm/fry/os_db.rb +35 -0
  13. data/lib/fpm/fry/plugin/alternatives.rb +90 -0
  14. data/lib/fpm/fry/plugin/edit_staging.rb +66 -0
  15. data/lib/fpm/fry/plugin/exclude.rb +18 -0
  16. data/lib/fpm/fry/plugin/init.rb +53 -0
  17. data/lib/fpm/fry/plugin/platforms.rb +10 -0
  18. data/lib/fpm/fry/plugin/script_helper.rb +176 -0
  19. data/lib/fpm/fry/plugin/service.rb +100 -0
  20. data/lib/fpm/fry/plugin.rb +3 -0
  21. data/lib/fpm/fry/recipe/builder.rb +267 -0
  22. data/lib/fpm/fry/recipe.rb +141 -0
  23. data/lib/fpm/fry/source/dir.rb +56 -0
  24. data/lib/fpm/fry/source/git.rb +90 -0
  25. data/lib/fpm/fry/source/package.rb +202 -0
  26. data/lib/fpm/fry/source/patched.rb +118 -0
  27. data/lib/fpm/fry/source.rb +47 -0
  28. data/lib/fpm/fry/stream_parser.rb +98 -0
  29. data/lib/fpm/fry/tar.rb +71 -0
  30. data/lib/fpm/fry/templates/debian/after_install.erb +9 -0
  31. data/lib/fpm/fry/templates/debian/before_install.erb +13 -0
  32. data/lib/fpm/fry/templates/debian/before_remove.erb +13 -0
  33. data/lib/fpm/fry/templates/redhat/after_install.erb +2 -0
  34. data/lib/fpm/fry/templates/redhat/before_install.erb +6 -0
  35. data/lib/fpm/fry/templates/redhat/before_remove.erb +6 -0
  36. data/lib/fpm/fry/templates/sysv.erb +125 -0
  37. data/lib/fpm/fry/templates/upstart.erb +15 -0
  38. data/lib/fpm/fry/ui.rb +12 -0
  39. data/lib/fpm/package/docker.rb +186 -0
  40. metadata +111 -0
@@ -0,0 +1,56 @@
1
+ require 'fpm/fry/source'
2
+ require 'fileutils'
3
+ require 'digest'
4
+ module FPM; module Fry ; module Source
5
+ class Dir
6
+
7
+ REGEX = %r!\A(?:file:|/|\.)!
8
+
9
+ def self.guess( url )
10
+ Source::guess_regex(REGEX, url)
11
+ end
12
+
13
+ class Cache < Struct.new(:package, :dir)
14
+ extend Forwardable
15
+
16
+ def_delegators :package, :url, :logger, :file_map
17
+
18
+ def tar_io
19
+ cmd = ['tar','-c','.']
20
+ logger.debug("Running tar",cmd: cmd, dir: dir)
21
+ ::Dir.chdir(dir) do
22
+ return IO.popen(cmd)
23
+ end
24
+ end
25
+
26
+ def copy_to(dst)
27
+ children = ::Dir.new(dir).select{|x| x[0...1] != "." }.map{|x| File.join(dir,x) }
28
+ FileUtils.cp_r(children, dst)
29
+ end
30
+
31
+ def cachekey
32
+ dig = Digest::SHA2.new
33
+ tar_io.each(1024) do |block|
34
+ dig << block
35
+ end
36
+ return dig.hexdigest
37
+ end
38
+ end
39
+
40
+ attr :url, :logger, :file_map
41
+
42
+ def initialize( url, options = {} )
43
+ @url = URI(url)
44
+ if @url.relative?
45
+ @url.path = File.expand_path(@url.path)
46
+ end
47
+ @logger = options.fetch(:logger){ Cabin::Channel.get }
48
+ @file_map = options.fetch(:file_map){ {'' => ''} }
49
+ end
50
+
51
+ def build_cache(_)
52
+ Cache.new(self, url.path)
53
+ end
54
+ end
55
+ end ; end ; end
56
+
@@ -0,0 +1,90 @@
1
+ require 'fileutils'
2
+ require 'forwardable'
3
+ require 'open3'
4
+ require 'fpm/fry/source'
5
+ module FPM; module Fry ; module Source
6
+ class Git
7
+
8
+ REGEX = %r!\A(?:git:|\S+@\S+:\S+\.git\z|https?:.*\.git\z|ssh:.*\.git\z)!
9
+
10
+ def self.guess( url )
11
+ Source::guess_regex(REGEX, url)
12
+ end
13
+
14
+ class Cache < Struct.new(:package, :tempdir)
15
+ extend Forwardable
16
+
17
+ def_delegators :package, :url, :rev, :logger, :file_map
18
+
19
+ def update
20
+ begin
21
+ if !File.exists? repodir
22
+ if git('init', '--bare') != 0
23
+ raise "Initializing git repository failed"
24
+ end
25
+ end
26
+ if git('fetch','--depth=1', url.to_s, rev) != 0
27
+ raise "Failed to fetch from remote #{url.to_s} ( #{rev} )"
28
+ end
29
+ return self
30
+ rescue Errno::ENOENT
31
+ raise "Cannot find git binary. Is it installed?"
32
+ end
33
+ end
34
+
35
+ def tar_io
36
+ cmd = [package.git, "--git-dir=#{repodir}",'archive','--format=tar','FETCH_HEAD']
37
+ logger.debug("Running git",cmd: cmd)
38
+ IO.popen(cmd)
39
+ end
40
+
41
+ def copy_to(dst)
42
+ cmd = [package.git, "--git-dir=#{repodir}", "--work-tree=#{dst}",'checkout','FETCH_HEAD','--','*']
43
+ logger.debug("Running git",cmd: cmd)
44
+ system(*cmd, chdir: dst)
45
+ end
46
+
47
+ def cachekey
48
+ cmd = [package.git, "--git-dir=#{repodir}",'rev-parse','FETCH_HEAD^{tree}']
49
+ logger.debug("Running git",cmd: cmd)
50
+ return IO.popen(cmd).read.chomp
51
+ end
52
+ private
53
+ def repodir
54
+ File.join(tempdir,File.basename(url.path))
55
+ end
56
+
57
+ def git(*args)
58
+ cmd = [package.git, "--git-dir=#{repodir}",*args]
59
+ logger.debug("Running git",cmd: cmd.join(' '))
60
+ Open3.popen3(*cmd) do |sin, out, err, thr|
61
+ sin.close
62
+ out.each_line do |line|
63
+ logger.debug(line.chomp)
64
+ end
65
+ err.each_line do |line|
66
+ logger.debug(line.chomp)
67
+ end
68
+ return thr.value
69
+ end
70
+ end
71
+
72
+ end
73
+
74
+ attr :logger, :git, :rev, :file_map, :url
75
+
76
+ def initialize( url, options = {} )
77
+ url = url.sub(/\A(\S+@\S+):(\S+\.git)\z/,'ssh://\1/\2')
78
+ @url = URI(url)
79
+ @logger = options.fetch(:logger){ Cabin::Channel.get }
80
+ @rev = options[:branch] || options[:tag] || options[:rev] || 'HEAD'
81
+ @file_map = options.fetch(:file_map){ {'' => ''} }
82
+ @git = options[:git] || 'git'
83
+ end
84
+
85
+ def build_cache(tempdir)
86
+ Cache.new(self, tempdir).update
87
+ end
88
+
89
+ end
90
+ end ; end ; end
@@ -0,0 +1,202 @@
1
+ require 'uri'
2
+ require 'digest'
3
+ require 'net/http'
4
+ require 'forwardable'
5
+ require 'zlib'
6
+ require 'fpm/fry/source'
7
+ module FPM; module Fry ; module Source
8
+ class Package
9
+
10
+ REGEX = %r!\Ahttps?:!
11
+
12
+ def self.guess( url )
13
+ Source::guess_regex(REGEX, url)
14
+ end
15
+
16
+ class RedirectError < CacheFailed
17
+ end
18
+
19
+ class Cache < Struct.new(:package,:tempdir)
20
+ extend Forwardable
21
+
22
+ def_delegators :package, :url, :checksum, :checksum_algorithm, :agent, :logger, :file_map
23
+
24
+ def initialize(*_)
25
+ super
26
+ if !checksum
27
+ update!
28
+ end
29
+ end
30
+
31
+ def cache_valid?
32
+ c = @observed_checksum || checksum
33
+ begin
34
+ checksum_algorithm.file(tempfile).hexdigest == c
35
+ rescue Errno::ENOENT
36
+ return false
37
+ end
38
+ end
39
+
40
+ def update!
41
+ if cache_valid?
42
+ logger.debug("Found valid cache", url: url, tempfile: tempfile)
43
+ return
44
+ end
45
+ d = checksum_algorithm.new
46
+ f = nil
47
+ fetch_url(url) do |resp|
48
+ begin
49
+ f = File.new(tempfile,'w')
50
+ resp.read_body do | chunk |
51
+ d.update(chunk)
52
+ f.write(chunk)
53
+ end
54
+ rescue => e
55
+ raise CacheFailed, e
56
+ ensure
57
+ f.close
58
+ end
59
+ end
60
+
61
+ @observed_checksum = d.hexdigest
62
+ logger.debug("got checksum", checksum: @observed_checksum)
63
+ if checksum
64
+ if d.hexdigest != checksum
65
+ raise CacheFailed.new("Checksum failed",given: d.hexdigest, expected: checksum)
66
+ end
67
+ else
68
+ return true
69
+ end
70
+ end
71
+
72
+ def fetch_url( url, redirs = 3, &block)
73
+ url = URI(url.to_s) unless url.kind_of? URI
74
+ Net::HTTP.get_response(url) do |resp|
75
+ case(resp)
76
+ when Net::HTTPRedirection
77
+ if redirs == 0
78
+ raise RedirectError, "Too many redirects"
79
+ end
80
+ logger.debug("Following redirect", url: url.to_s , location: resp['location'])
81
+ return fetch_url( resp['location'], redirs - 1, &block)
82
+ when Net::HTTPSuccess
83
+ return block.call(resp)
84
+ else
85
+ raise CacheFailed.new('Unable to fetch file',url: url.to_s, http_code: resp.code, http_message: resp.message)
86
+ end
87
+ end
88
+ end
89
+
90
+ def tempfile
91
+ File.join(tempdir,File.basename(url.path))
92
+ end
93
+
94
+ def cachekey
95
+ @observed_checksum || checksum
96
+ end
97
+
98
+ end
99
+
100
+ class TarCache < Cache
101
+
102
+ def tar_io
103
+ update!
104
+ ioclass.open(tempfile)
105
+ end
106
+
107
+ def copy_to(dst)
108
+ update!
109
+ cmd = ['tar','-xf',tempfile,'-C',dst]
110
+ logger.debug("Running tar",cmd: cmd)
111
+ system(*cmd)
112
+ end
113
+
114
+ protected
115
+ def ioclass
116
+ File
117
+ end
118
+ end
119
+
120
+ class TarGzCache < TarCache
121
+ protected
122
+
123
+ def ioclass
124
+ Zlib::GzipReader
125
+ end
126
+ end
127
+
128
+ class ZipCache < Cache
129
+
130
+ def tar_io
131
+ if !::File.directory?( unpacked_tmpdir )
132
+ workdir = unpacked_tmpdir + '.tmp'
133
+ begin
134
+ FileUtils.mkdir(workdir)
135
+ rescue Errno::EEXIST
136
+ FileUtils.rm_rf(workdir)
137
+ FileUtils.mkdir(workdir)
138
+ end
139
+ copy_to( workdir )
140
+ File.rename(workdir, unpacked_tmpdir)
141
+ end
142
+ cmd = ['tar','-c','.']
143
+ logger.debug("Running tar",cmd: cmd, dir: unpacked_tmpdir)
144
+ ::Dir.chdir(unpacked_tmpdir) do
145
+ return IO.popen(cmd)
146
+ end
147
+ end
148
+
149
+ def copy_to(dst)
150
+ update!
151
+ cmd = ['unzip', tempfile, '-d', dst ]
152
+ logger.debug("Running unzip",cmd: cmd)
153
+ system(*cmd, out: '/dev/null')
154
+ end
155
+
156
+ def unpacked_tmpdir
157
+ File.join(tempdir, cachekey)
158
+ end
159
+ end
160
+
161
+ CACHE_CLASSES = {
162
+ '.tar' => TarCache,
163
+ '.tar.gz' => TarGzCache,
164
+ '.tgz' => TarGzCache,
165
+ '.zip' => ZipCache
166
+ }
167
+
168
+ attr :file_map, :data, :url, :extension, :checksum, :checksum_algorithm, :agent, :logger
169
+
170
+ def initialize( url, options = {} )
171
+ @url = URI(url)
172
+ @extension = options.fetch(:extension){
173
+ CACHE_CLASSES.keys.find{|ext|
174
+ @url.path.end_with?(ext)
175
+ }
176
+ }
177
+ @logger = options.fetch(:logger){ Cabin::Channel.get }
178
+ @checksum = options[:checksum]
179
+ @checksum_algorithm = guess_checksum_algorithm(options[:checksum])
180
+ @file_map = options.fetch(:file_map){ {'' => ''} }
181
+ end
182
+
183
+ def build_cache(tempdir)
184
+ CACHE_CLASSES.fetch(extension).new(self, tempdir)
185
+ end
186
+ private
187
+
188
+ def guess_checksum_algorithm( checksum )
189
+ case(checksum)
190
+ when nil
191
+ return Digest::SHA256
192
+ when /\A(sha256:)?[0-9a-f]{64}\z/ then
193
+ return Digest::SHA256
194
+ when /\A(sha1:)?[0-9a-f]{40}\z/ then
195
+ return Digest::SHA1
196
+ else
197
+ raise "Unknown checksum algorithm"
198
+ end
199
+ end
200
+
201
+ end
202
+ end end end
@@ -0,0 +1,118 @@
1
+ require 'fpm/fry/tar'
2
+ require 'digest'
3
+ module FPM; module Fry ; module Source
4
+ class Patched
5
+
6
+ class Cache < Struct.new(:package, :tmpdir)
7
+ extend Forwardable
8
+
9
+ def_delegators :package, :logger, :file_map
10
+
11
+ attr :inner
12
+
13
+ def initialize(*_)
14
+ @updated = false
15
+ super
16
+ @inner = package.inner.build_cache(tmpdir)
17
+ end
18
+
19
+ def update!
20
+ @updated ||= begin
21
+ if !File.directory?(unpacked_tmpdir)
22
+ workdir = unpacked_tmpdir + '.tmp'
23
+ begin
24
+ FileUtils.mkdir(workdir)
25
+ rescue Errno::EEXIST
26
+ FileUtils.rm_rf(workdir)
27
+ FileUtils.mkdir(workdir)
28
+ end
29
+ if inner.respond_to? :copy_to
30
+ inner.copy_to(workdir)
31
+ else
32
+ ex = Tar::Extractor.new(logger: logger)
33
+ tio = inner.tar_io
34
+ begin
35
+ ex.extract(workdir, ::Gem::Package::TarReader.new(tio), chown: false)
36
+ ensure
37
+ tio.close
38
+ end
39
+ end
40
+ package.patches.each do |patch|
41
+ cmd = ['patch','-p1','-i',patch[:file]]
42
+ chdir = File.expand_path(patch.fetch(:chdir,'.'),workdir)
43
+ logger.debug("Running patch",cmd: cmd, dir: chdir )
44
+ system(*cmd, chdir: chdir, out: :close)
45
+ end
46
+ File.rename(workdir, unpacked_tmpdir)
47
+ end
48
+ true
49
+ end
50
+ end
51
+ private :update!
52
+
53
+ def tar_io
54
+ update!
55
+ cmd = ['tar','-c','.']
56
+ logger.debug("Running tar",cmd: cmd, dir: unpacked_tmpdir)
57
+ # IO.popen( ..., chdir: ... ) doesn't work on older ruby
58
+ ::Dir.chdir(unpacked_tmpdir) do
59
+ return IO.popen(cmd)
60
+ end
61
+ end
62
+
63
+ def cachekey
64
+ dig = Digest::SHA2.new
65
+ dig << inner.cachekey << "\x00"
66
+ package.patches.each do |patch|
67
+ dig.file(patch[:file])
68
+ dig << "\x00"
69
+ end
70
+ return dig.hexdigest
71
+ end
72
+
73
+ def unpacked_tmpdir
74
+ File.join(tmpdir, cachekey)
75
+ end
76
+
77
+ end
78
+
79
+ attr :inner, :patches
80
+
81
+ extend Forwardable
82
+
83
+ def_delegators :inner, :logger, :file_map
84
+
85
+ def initialize( inner , options = {})
86
+ @inner = inner
87
+ @patches = Array(options[:patches]).map do |file|
88
+ if file.kind_of? String
89
+ options = {file: file}
90
+ elsif file.kind_of? Hash
91
+ options = file.dup
92
+ else
93
+ raise ArgumentError, "Expected a Hash or a String, got #{file.inspect}"
94
+ end
95
+ options[:file] = File.expand_path(options[:file])
96
+ if !File.exists?(options[:file])
97
+ raise ArgumentError, "File doesn't exist: #{options[:file]}"
98
+ end
99
+ options
100
+ end
101
+ end
102
+
103
+ def build_cache(tmpdir)
104
+ Cache.new(self,tmpdir)
105
+ end
106
+
107
+ def self.decorate(options)
108
+ if options.key?(:patches) && Array(options[:patches]).size > 0
109
+ p = options.delete(:patches)
110
+ return new( yield(options), patches: p )
111
+ else
112
+ return yield options
113
+ end
114
+ end
115
+ end
116
+
117
+ end ; end ; end
118
+
@@ -0,0 +1,47 @@
1
+ module FPM; module Fry ; module Source
2
+
3
+ class CacheFailed < StandardError
4
+
5
+ attr :options
6
+
7
+ def initialize(e, opts = {})
8
+ if e.kind_of? Exception
9
+ @options = {reason: e}.merge opts
10
+ super(e.message)
11
+ else
12
+ @options = opts.dup
13
+ super(e.to_s)
14
+ end
15
+ end
16
+ end
17
+
18
+ module Null
19
+
20
+ module Cache
21
+ def self.tar_io
22
+ StringIO.new("\x00"*1024)
23
+ end
24
+ def self.file_map
25
+ return {}
26
+ end
27
+ def self.cachekey
28
+ return '0' * 32
29
+ end
30
+ end
31
+
32
+ def self.build_cache(*_)
33
+ return Cache
34
+ end
35
+
36
+ end
37
+
38
+ class << self
39
+
40
+ def guess_regex(rx, url)
41
+ if m = rx.match(url.to_s)
42
+ return m[0].size
43
+ end
44
+ end
45
+
46
+ end
47
+ end ; end ; end
@@ -0,0 +1,98 @@
1
+ require 'excon/middlewares/base'
2
+ module FPM; module Fry
3
+ class StreamParser
4
+
5
+ class ShortRead < EOFError
6
+ end
7
+
8
+ class Instance < Excon::Middleware::Base
9
+
10
+ def initialize(stack, parser)
11
+ super(stack)
12
+ @parser = parser
13
+ end
14
+
15
+ def response_call(datum)
16
+ if datum[:response]
17
+ # probably mocked
18
+ if datum[:response][:body]
19
+ @parser.parse(StringIO.new(datum[:response][:body]))
20
+ end
21
+ return @stack.response_call(datum)
22
+ else
23
+ socket = datum[:connection].send(:socket)
24
+ begin
25
+ line = socket.readline
26
+ match = /^HTTP\/\d+\.\d+\s(\d{3})\s/.match(line)
27
+ end while !match
28
+ status = match[1].to_i
29
+
30
+ datum[:response] = {
31
+ :body => '',
32
+ :headers => Excon::Headers.new,
33
+ :status => status,
34
+ :remote_ip => socket.respond_to?(:remote_ip) && socket.remote_ip,
35
+ }
36
+ Excon::Response.parse_headers(socket, datum)
37
+
38
+ @parser.parse(socket)
39
+ return @stack.response_call(datum)
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+ attr :out, :err
46
+
47
+ def initialize(out, err)
48
+ @out, @err = out, err
49
+ @state = :null
50
+ @left = 0
51
+ end
52
+
53
+ def new(stack)
54
+ Instance.new(stack, self)
55
+ end
56
+
57
+ def parse(socket)
58
+ left = 0
59
+ streams = {1 => out, 2 => err}
60
+ loop do
61
+ type = read_exactly(socket,4){|part|
62
+ if part.bytesize == 0
63
+ return
64
+ else
65
+ raise ShortRead
66
+ end
67
+ }.unpack("c".freeze)[0]
68
+ stream = streams.fetch(type){ raise ArgumentError, "Wrong stream type: #{type}"}
69
+ len = read_exactly(socket,4).unpack('I>')[0]
70
+ while len > 0
71
+ chunk = socket.read([64,len].min)
72
+ raise ShortRead if chunk.nil?
73
+ len -= chunk.bytesize
74
+ stream.write(chunk)
75
+ end
76
+ end
77
+ end
78
+
79
+ def read_exactly(socket, len)
80
+ buf = ""
81
+ left = len
82
+ while left != 0
83
+ read = socket.read(left)
84
+ if read.nil?
85
+ if block_given?
86
+ yield buf
87
+ else
88
+ raise ShortRead
89
+ end
90
+ end
91
+ buf << read
92
+ left = len - buf.bytesize
93
+ end
94
+ return buf
95
+ end
96
+
97
+ end
98
+ end ; end
@@ -0,0 +1,71 @@
1
+ module FPM; module Fry; end ; end
2
+
3
+ class FPM::Fry::Tar
4
+
5
+ class Extractor
6
+
7
+ def initialize( options = {} )
8
+ @logger = options.fetch(:logger){ Cabin::Channel.get }
9
+ end
10
+
11
+ def extract(destdir, reader, options = {})
12
+ reader.each do |entry|
13
+ extract_entry(File.join(destdir, entry.full_name), entry, options)
14
+ end
15
+ end
16
+
17
+ def extract_entry(dest, entry, options = {})
18
+ full_name = entry.full_name
19
+ mode = entry.header.mode
20
+
21
+ destdir = File.dirname(dest)
22
+ uid = map_user(entry.header.uid, entry.header.uname)
23
+ gid = map_group(entry.header.gid, entry.header.gname)
24
+
25
+ @logger.debug('Extracting','file' => dest, 'uid'=> uid, 'gid' => gid, 'entry.fullname' => full_name, 'entry.mode' => mode )
26
+
27
+ case(entry.header.typeflag)
28
+ when "5" # Directory
29
+ FileUtils.mkdir_p(dest, :mode => mode)
30
+ when "2" # Symlink
31
+ destdir = File.dirname(dest)
32
+ FileUtils.mkdir_p(destdir, :mode => 0755)
33
+ File.symlink( entry.header.linkname, dest )
34
+ when "0" # File
35
+ destdir = File.dirname(dest)
36
+ FileUtils.mkdir_p(destdir, :mode => 0755)
37
+ File.open(dest, "wb", entry.header.mode) do |os|
38
+ loop do
39
+ data = entry.read(4096)
40
+ break unless data
41
+ os.write(data)
42
+ end
43
+ os.fsync
44
+ end
45
+ else
46
+ @logger.warn('Ignoring unknown tar entry',name: full_name)
47
+ return
48
+ end
49
+ FileUtils.chmod(entry.header.mode, dest)
50
+ chown( uid, gid, dest ) if options.fetch(:chown,true)
51
+ end
52
+
53
+ def chown( uid, gid, path )
54
+ FileUtils.chown( uid, gid, path )
55
+ rescue Errno::EPERM
56
+ @logger.warn('Unable to chown file', 'file' => path, 'uid' => uid, 'gid' => gid)
57
+ end
58
+
59
+ def map_user( uid, _ )
60
+ return uid
61
+ end
62
+
63
+ def map_group( gid, _ )
64
+ return gid
65
+ end
66
+
67
+ end
68
+
69
+ end
70
+
71
+
@@ -0,0 +1,9 @@
1
+ #!/bin/bash
2
+ case "$1" in
3
+ configure)
4
+ <%= configure.join("\n") %>
5
+ ;;
6
+ *)
7
+ exit 1
8
+ ;;
9
+ esac
@@ -0,0 +1,13 @@
1
+ #!/bin/bash
2
+
3
+ case "$1" in
4
+ install)
5
+ <%= install.join("\n") %>
6
+ ;;
7
+ upgrade)
8
+ <%= upgrade.join("\n") %>
9
+ ;;
10
+ *)
11
+ exit 1
12
+ ;;
13
+ esac