fpm-fry 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/fpm-fry +10 -0
- data/lib/cabin/nice_output.rb +70 -0
- data/lib/fpm/fry/block_enumerator.rb +25 -0
- data/lib/fpm/fry/build_output_parser.rb +22 -0
- data/lib/fpm/fry/client.rb +162 -0
- data/lib/fpm/fry/command/cook.rb +370 -0
- data/lib/fpm/fry/command.rb +90 -0
- data/lib/fpm/fry/detector.rb +109 -0
- data/lib/fpm/fry/docker_file.rb +149 -0
- data/lib/fpm/fry/joined_io.rb +63 -0
- data/lib/fpm/fry/os_db.rb +35 -0
- data/lib/fpm/fry/plugin/alternatives.rb +90 -0
- data/lib/fpm/fry/plugin/edit_staging.rb +66 -0
- data/lib/fpm/fry/plugin/exclude.rb +18 -0
- data/lib/fpm/fry/plugin/init.rb +53 -0
- data/lib/fpm/fry/plugin/platforms.rb +10 -0
- data/lib/fpm/fry/plugin/script_helper.rb +176 -0
- data/lib/fpm/fry/plugin/service.rb +100 -0
- data/lib/fpm/fry/plugin.rb +3 -0
- data/lib/fpm/fry/recipe/builder.rb +267 -0
- data/lib/fpm/fry/recipe.rb +141 -0
- data/lib/fpm/fry/source/dir.rb +56 -0
- data/lib/fpm/fry/source/git.rb +90 -0
- data/lib/fpm/fry/source/package.rb +202 -0
- data/lib/fpm/fry/source/patched.rb +118 -0
- data/lib/fpm/fry/source.rb +47 -0
- data/lib/fpm/fry/stream_parser.rb +98 -0
- data/lib/fpm/fry/tar.rb +71 -0
- data/lib/fpm/fry/templates/debian/after_install.erb +9 -0
- data/lib/fpm/fry/templates/debian/before_install.erb +13 -0
- data/lib/fpm/fry/templates/debian/before_remove.erb +13 -0
- data/lib/fpm/fry/templates/redhat/after_install.erb +2 -0
- data/lib/fpm/fry/templates/redhat/before_install.erb +6 -0
- data/lib/fpm/fry/templates/redhat/before_remove.erb +6 -0
- data/lib/fpm/fry/templates/sysv.erb +125 -0
- data/lib/fpm/fry/templates/upstart.erb +15 -0
- data/lib/fpm/fry/ui.rb +12 -0
- data/lib/fpm/package/docker.rb +186 -0
- metadata +111 -0
@@ -0,0 +1,176 @@
|
|
1
|
+
require 'fpm/fry/plugin'
|
2
|
+
module FPM::Fry::Plugin::ScriptHelper
|
3
|
+
|
4
|
+
class Script
|
5
|
+
|
6
|
+
attr :renderer
|
7
|
+
|
8
|
+
def initialize(renderer)
|
9
|
+
@renderer = renderer
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
renderer.call(self)
|
14
|
+
end
|
15
|
+
|
16
|
+
def name
|
17
|
+
self.class.name.split('::').last.gsub(/([^A-Z])([A-Z])/,'\1_\2').downcase
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module RenderErb
|
22
|
+
def render_path(script, path)
|
23
|
+
_erbout = ""
|
24
|
+
erb = ERB.new(
|
25
|
+
IO.read(File.join(File.dirname(__FILE__),'..','templates',path, "#{script.name}.erb"))
|
26
|
+
)
|
27
|
+
script.instance_eval(erb.src)
|
28
|
+
return _erbout
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module DebianRenderer
|
33
|
+
extend RenderErb
|
34
|
+
def self.call(script)
|
35
|
+
render_path(script,'debian')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module RedhatRenderer
|
40
|
+
extend RenderErb
|
41
|
+
def self.call(script)
|
42
|
+
render_path(script,'redhat')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class BeforeInstall < Script
|
47
|
+
|
48
|
+
# deb: $1 == install
|
49
|
+
# rpm: $1 == 1
|
50
|
+
attr :install
|
51
|
+
|
52
|
+
# deb: $1 == upgrade
|
53
|
+
# rpm: $1 >= 2
|
54
|
+
attr :upgrade
|
55
|
+
|
56
|
+
def initialize(*_)
|
57
|
+
super
|
58
|
+
@install = []
|
59
|
+
@upgrade = []
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
class AfterInstall < Script
|
65
|
+
|
66
|
+
def initialize(*_)
|
67
|
+
super
|
68
|
+
@configure = []
|
69
|
+
end
|
70
|
+
|
71
|
+
# deb: $1 == configure
|
72
|
+
# rpm: -always-
|
73
|
+
attr :configure
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
class BeforeRemove < Script
|
78
|
+
|
79
|
+
def initialize(*_)
|
80
|
+
super
|
81
|
+
@remove = []
|
82
|
+
@upgrade = []
|
83
|
+
end
|
84
|
+
|
85
|
+
# deb: $1 == remove
|
86
|
+
# rpm: $1 == 0
|
87
|
+
attr :remove
|
88
|
+
|
89
|
+
# deb: $1 == upgrade
|
90
|
+
# rpm: $1 >= 1
|
91
|
+
attr :upgrade
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
class AfterRemove < Script
|
96
|
+
|
97
|
+
def initialize
|
98
|
+
@remove = []
|
99
|
+
@upgrade = []
|
100
|
+
end
|
101
|
+
|
102
|
+
# deb: $1 == upgrade
|
103
|
+
# rpm: $1 == 1
|
104
|
+
attr :upgrade
|
105
|
+
|
106
|
+
# deb: $1 == remove
|
107
|
+
# rpm: $1 == 0
|
108
|
+
attr :remove
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
NAME_TO_SCRIPT = {
|
113
|
+
before_install: BeforeInstall,
|
114
|
+
after_install: AfterInstall,
|
115
|
+
before_remove: BeforeRemove,
|
116
|
+
after_remove: AfterRemove
|
117
|
+
}
|
118
|
+
|
119
|
+
SCRIPT_TO_NAME = NAME_TO_SCRIPT.invert
|
120
|
+
|
121
|
+
class DSL < Struct.new(:builder)
|
122
|
+
|
123
|
+
# before(install) => before_install:install
|
124
|
+
# before(upgrade) => before_install:upgrade
|
125
|
+
# after(install_or_upgrade) => after_install:configure
|
126
|
+
# before(remove_for_upgrade) => before_remove:upgrade
|
127
|
+
# before(remove) => before_remove:remove
|
128
|
+
# after(remove) => after_remove:remove
|
129
|
+
# after(remove_for_upgrade) => after_remove:upgrade
|
130
|
+
|
131
|
+
def after_install_or_upgrade(*scripts)
|
132
|
+
find(:after_install).configure.push(*scripts)
|
133
|
+
end
|
134
|
+
|
135
|
+
def before_remove_entirely(*scripts)
|
136
|
+
find(:before_remove).remove.push(*scripts)
|
137
|
+
end
|
138
|
+
|
139
|
+
def after_remove_entirely(*scripts)
|
140
|
+
find(:after_remove).remove.push(*scripts)
|
141
|
+
end
|
142
|
+
private
|
143
|
+
|
144
|
+
def find(type)
|
145
|
+
klass = NAME_TO_SCRIPT[type]
|
146
|
+
script = builder.script(type).find{|s| s.kind_of? klass }
|
147
|
+
if script.nil?
|
148
|
+
script = klass.new( renderer )
|
149
|
+
builder.script(type,script)
|
150
|
+
end
|
151
|
+
return script
|
152
|
+
end
|
153
|
+
|
154
|
+
def renderer
|
155
|
+
@renderer ||= case(builder.flavour)
|
156
|
+
when 'debian' then DebianRenderer
|
157
|
+
when 'redhat' then RedhatRenderer
|
158
|
+
else
|
159
|
+
raise "Unknown flavour: #{builder.flavour.inspect}"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
def self.apply(builder, options = {}, &block)
|
166
|
+
dsl = DSL.new(builder)
|
167
|
+
if block
|
168
|
+
if block.arity == 1
|
169
|
+
yield dsl
|
170
|
+
else
|
171
|
+
dsl.instance_eval(&block)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'fpm/fry/plugin'
|
2
|
+
require 'fpm/fry/plugin/init'
|
3
|
+
require 'fpm/fry/plugin/edit_staging'
|
4
|
+
require 'erb'
|
5
|
+
require 'shellwords'
|
6
|
+
module FPM::Fry::Plugin ; module Service
|
7
|
+
|
8
|
+
class Environment < Struct.new(:name,:command, :description)
|
9
|
+
|
10
|
+
def render(file)
|
11
|
+
_erbout = ""
|
12
|
+
erb = ERB.new(
|
13
|
+
IO.read(File.join(File.dirname(__FILE__),'..','templates',file))
|
14
|
+
)
|
15
|
+
eval(erb.src)
|
16
|
+
return _erbout
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
class DSL
|
22
|
+
|
23
|
+
def initialize(*_)
|
24
|
+
super
|
25
|
+
@name = nil
|
26
|
+
@command = []
|
27
|
+
end
|
28
|
+
|
29
|
+
def name( n = nil )
|
30
|
+
if n
|
31
|
+
@name = n
|
32
|
+
end
|
33
|
+
return @name
|
34
|
+
end
|
35
|
+
|
36
|
+
def command( *args )
|
37
|
+
if args.any?
|
38
|
+
@command = args
|
39
|
+
end
|
40
|
+
return @command
|
41
|
+
end
|
42
|
+
|
43
|
+
# @api private
|
44
|
+
def add!(builder)
|
45
|
+
name = self.name || builder.name || raise
|
46
|
+
init = Init.detect_init(builder.variables)
|
47
|
+
edit = builder.plugin('edit_staging')
|
48
|
+
env = Environment.new(name, command, "")
|
49
|
+
case(init)
|
50
|
+
when 'upstart' then
|
51
|
+
edit.add_file "/etc/init/#{name}.conf",StringIO.new( env.render('upstart.erb') )
|
52
|
+
edit.ln_s '/lib/init/upstart-job', "/etc/init.d/#{name}"
|
53
|
+
builder.plugin('script_helper') do |sh|
|
54
|
+
sh.after_install_or_upgrade(<<BASH)
|
55
|
+
if status #{Shellwords.shellescape name} 2>/dev/null | grep -q ' start/'; then
|
56
|
+
# It has to be stop+start because upstart doesn't pickup changes with restart.
|
57
|
+
stop #{Shellwords.shellescape name}
|
58
|
+
fi
|
59
|
+
start #{Shellwords.shellescape name}
|
60
|
+
BASH
|
61
|
+
sh.before_remove_entirely(<<BASH)
|
62
|
+
if status #{Shellwords.shellescape name} 2>/dev/null | grep -q ' start/'; then
|
63
|
+
stop #{Shellwords.shellescape name}
|
64
|
+
fi
|
65
|
+
BASH
|
66
|
+
end
|
67
|
+
when 'sysv' then
|
68
|
+
edit.add_file "/etc/init.d/#{name}",StringIO.new( env.render('sysv.erb') ), chmod: '750'
|
69
|
+
builder.plugin('script_helper') do |sh|
|
70
|
+
sh.after_install_or_upgrade(<<BASH)
|
71
|
+
update-rc.d #{Shellwords.shellescape name} defaults
|
72
|
+
/etc/init.d/#{Shellwords.shellescape name} restart
|
73
|
+
BASH
|
74
|
+
sh.before_remove_entirely(<<BASH)
|
75
|
+
/etc/init.d/#{Shellwords.shellescape name} stop
|
76
|
+
update-rc.d -f #{Shellwords.shellescape name} remove
|
77
|
+
BASH
|
78
|
+
end
|
79
|
+
when 'systemd' then
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.apply(builder, &block)
|
87
|
+
d = DSL.new
|
88
|
+
if !block
|
89
|
+
raise ArgumentError, "service plugin requires a block"
|
90
|
+
elsif block.arity == 1
|
91
|
+
block.call(d)
|
92
|
+
else
|
93
|
+
d.instance_eval(&block)
|
94
|
+
end
|
95
|
+
d.add!(builder)
|
96
|
+
return nil
|
97
|
+
end
|
98
|
+
|
99
|
+
end end
|
100
|
+
|
@@ -0,0 +1,267 @@
|
|
1
|
+
require 'fpm/fry/recipe'
|
2
|
+
require 'forwardable'
|
3
|
+
module FPM::Fry
|
4
|
+
class Recipe
|
5
|
+
|
6
|
+
class NotFound < StandardError
|
7
|
+
end
|
8
|
+
|
9
|
+
class PackageBuilder < Struct.new(:variables, :package_recipe)
|
10
|
+
|
11
|
+
attr :logger
|
12
|
+
|
13
|
+
def initialize( variables, recipe = PackageRecipe.new, options = {})
|
14
|
+
super(variables, recipe)
|
15
|
+
@logger = options.fetch(:logger){ Cabin::Channel.get }
|
16
|
+
end
|
17
|
+
|
18
|
+
def flavour
|
19
|
+
variables[:flavour]
|
20
|
+
end
|
21
|
+
|
22
|
+
def distribution
|
23
|
+
variables[:distribution]
|
24
|
+
end
|
25
|
+
alias platform distribution
|
26
|
+
|
27
|
+
def distribution_version
|
28
|
+
variables[:distribution_version]
|
29
|
+
end
|
30
|
+
alias platform_version distribution_version
|
31
|
+
|
32
|
+
def codename
|
33
|
+
variables[:codename]
|
34
|
+
end
|
35
|
+
|
36
|
+
def iteration(value = Not)
|
37
|
+
get_or_set('@iteration',value)
|
38
|
+
end
|
39
|
+
alias revision iteration
|
40
|
+
|
41
|
+
def version(value = Not)
|
42
|
+
get_or_set('@version',value)
|
43
|
+
end
|
44
|
+
|
45
|
+
def name(value = Not)
|
46
|
+
get_or_set('@name',value)
|
47
|
+
end
|
48
|
+
|
49
|
+
def vendor(value = Not)
|
50
|
+
get_or_set('@vendor',value)
|
51
|
+
end
|
52
|
+
|
53
|
+
def depends( name , options = {} )
|
54
|
+
name, options = parse_package(name, options)
|
55
|
+
package_recipe.depends[name] = options
|
56
|
+
end
|
57
|
+
|
58
|
+
def conflicts( name , options = {} )
|
59
|
+
name, options = parse_package(name, options)
|
60
|
+
package_recipe.conflicts[name] = options
|
61
|
+
end
|
62
|
+
|
63
|
+
def provides( name , options = {} )
|
64
|
+
name, options = parse_package(name, options)
|
65
|
+
package_recipe.provides[name] = options
|
66
|
+
end
|
67
|
+
|
68
|
+
def replaces( name , options = {} )
|
69
|
+
name, options = parse_package(name, options)
|
70
|
+
package_recipe.replaces[name] = options
|
71
|
+
end
|
72
|
+
|
73
|
+
def files( pattern )
|
74
|
+
package_recipe.files << pattern
|
75
|
+
end
|
76
|
+
|
77
|
+
def plugin(name, *args, &block)
|
78
|
+
logger.debug('Loading Plugin', name: name, args: args, block: block, load_path: $LOAD_PATH)
|
79
|
+
if name =~ /\A\./
|
80
|
+
require name
|
81
|
+
else
|
82
|
+
require File.join('fpm/fry/plugin',name)
|
83
|
+
end
|
84
|
+
module_name = File.basename(name,'.rb').gsub(/(?:\A|_)([a-z])/){ $1.upcase }
|
85
|
+
mod = FPM::Fry::Plugin.const_get(module_name)
|
86
|
+
if mod.respond_to? :apply
|
87
|
+
mod.apply(self, *args, &block)
|
88
|
+
else
|
89
|
+
extend(mod)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def script(type, value = Not)
|
94
|
+
if value != Not
|
95
|
+
package_recipe.scripts[type] << value
|
96
|
+
end
|
97
|
+
return package_recipe.scripts[type]
|
98
|
+
end
|
99
|
+
|
100
|
+
def before_install(*args)
|
101
|
+
script(:before_install, *args)
|
102
|
+
end
|
103
|
+
alias pre_install before_install
|
104
|
+
alias preinstall before_install
|
105
|
+
|
106
|
+
def after_install(*args)
|
107
|
+
script(:after_install, *args)
|
108
|
+
end
|
109
|
+
alias post_install after_install
|
110
|
+
alias postinstall after_install
|
111
|
+
|
112
|
+
def before_remove(*args)
|
113
|
+
script(:before_remove, *args)
|
114
|
+
end
|
115
|
+
alias before_uninstall before_remove
|
116
|
+
alias pre_uninstall before_remove
|
117
|
+
alias preuninstall before_remove
|
118
|
+
|
119
|
+
def after_remove(*args)
|
120
|
+
script(:after_remove, *args)
|
121
|
+
end
|
122
|
+
alias after_uninstall after_remove
|
123
|
+
alias post_uninstall after_remove
|
124
|
+
alias postuninstall after_remove
|
125
|
+
|
126
|
+
def output_hooks
|
127
|
+
package_recipe.output_hooks
|
128
|
+
end
|
129
|
+
|
130
|
+
protected
|
131
|
+
|
132
|
+
def parse_package( name, options = {} )
|
133
|
+
if options.kind_of? String
|
134
|
+
options = {version: options}
|
135
|
+
end
|
136
|
+
case(v = options[:version])
|
137
|
+
when String
|
138
|
+
if v =~ /\A(<=|<<|>=|>>|<>|=|>|<)(\s*)/
|
139
|
+
options[:version] = ' ' + $1 + ' ' + $'
|
140
|
+
else
|
141
|
+
options[:version] = ' = ' + v
|
142
|
+
end
|
143
|
+
end
|
144
|
+
return name, options
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
Not = Module.new
|
149
|
+
def get_or_set(name, value = Not)
|
150
|
+
if value == Not
|
151
|
+
return package_recipe.instance_variable_get(name)
|
152
|
+
else
|
153
|
+
return package_recipe.instance_variable_set(name, value)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
class Builder < PackageBuilder
|
160
|
+
|
161
|
+
attr :recipe
|
162
|
+
|
163
|
+
def initialize( variables, recipe = Recipe.new, options = {})
|
164
|
+
variables = variables.dup
|
165
|
+
if variables[:distribution] && !variables[:flavour] && OsDb[variables[:distribution]]
|
166
|
+
variables[:flavour] = OsDb[variables[:distribution]][:flavour]
|
167
|
+
end
|
168
|
+
if !variables[:codename] && OsDb[variables[:distribution]] && variables[:distribution_version]
|
169
|
+
codename = OsDb[variables[:distribution]][:codenames].find{|name,version| variables[:distribution_version].start_with? version }
|
170
|
+
variables[:codename] = codename[0] if codename
|
171
|
+
end
|
172
|
+
variables.freeze
|
173
|
+
@recipe = recipe
|
174
|
+
super(variables, recipe.packages[0], options = {})
|
175
|
+
end
|
176
|
+
|
177
|
+
def load_file( file )
|
178
|
+
file = File.expand_path(file)
|
179
|
+
begin
|
180
|
+
content = IO.read(file)
|
181
|
+
rescue Errno::ENOENT => e
|
182
|
+
raise NotFound, e
|
183
|
+
end
|
184
|
+
basedir = File.dirname(file)
|
185
|
+
Dir.chdir(basedir) do
|
186
|
+
instance_eval(content,file,0)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def source( url , options = {} )
|
191
|
+
options = options.merge(logger: logger)
|
192
|
+
source = Source::Patched.decorate(options) do |options|
|
193
|
+
guess_source(url,options).new(url, options)
|
194
|
+
end
|
195
|
+
recipe.source = source
|
196
|
+
end
|
197
|
+
|
198
|
+
def run(*args)
|
199
|
+
if args.first.kind_of? Hash
|
200
|
+
options = args.shift
|
201
|
+
else
|
202
|
+
options = {}
|
203
|
+
end
|
204
|
+
command = args.shift
|
205
|
+
name = options.fetch(:name){ [command,*args].select{|c| c[0] != '-' }.join('-') }
|
206
|
+
recipe.steps[name] = Shellwords.join([command, *args])
|
207
|
+
end
|
208
|
+
|
209
|
+
def build_depends( name , options = {} )
|
210
|
+
name, options = parse_package(name, options)
|
211
|
+
recipe.build_depends[name] = options
|
212
|
+
end
|
213
|
+
|
214
|
+
def input_hooks
|
215
|
+
recipe.input_hooks
|
216
|
+
end
|
217
|
+
|
218
|
+
def package(name, &block)
|
219
|
+
pr = PackageRecipe.new
|
220
|
+
pr.name = name
|
221
|
+
pr.version = package_recipe.version
|
222
|
+
pr.iteration = package_recipe.iteration
|
223
|
+
recipe.packages << pr
|
224
|
+
PackageBuilder.new(variables, pr).instance_eval(&block)
|
225
|
+
end
|
226
|
+
|
227
|
+
protected
|
228
|
+
|
229
|
+
def source_types
|
230
|
+
@source_types ||= {
|
231
|
+
git: Source::Git,
|
232
|
+
http: Source::Package,
|
233
|
+
tar: Source::Package,
|
234
|
+
dir: Source::Dir
|
235
|
+
}
|
236
|
+
end
|
237
|
+
|
238
|
+
def register_source_type( name, klass )
|
239
|
+
if !klass.respond_to? :new
|
240
|
+
raise ArgumentError.new("Expected something that responds to :new, got #{klass.inspect}")
|
241
|
+
end
|
242
|
+
source_types[name] = klass
|
243
|
+
end
|
244
|
+
|
245
|
+
NEG_INF = (-1.0/0.0)
|
246
|
+
|
247
|
+
def guess_source( url, options = {} )
|
248
|
+
if w = options[:with]
|
249
|
+
return source_types.fetch(w){ raise ArgumentError.new("Unknown source type: #{w}") }
|
250
|
+
end
|
251
|
+
scores = source_types.values.uniq\
|
252
|
+
.select{|klass| klass.respond_to? :guess }\
|
253
|
+
.group_by{|klass| klass.guess(url) }\
|
254
|
+
.sort_by{|score,_| score.nil? ? NEG_INF : score }
|
255
|
+
score, klasses = scores.last
|
256
|
+
if score == nil
|
257
|
+
raise ArgumentError.new("No source provide found for #{url}.\nMaybe try explicitly setting the type using :with parameter. Valid options are: #{source_types.keys.join(', ')}")
|
258
|
+
end
|
259
|
+
if klasses.size != 1
|
260
|
+
raise ArgumentError.new("Multiple possible source providers found for #{url}: #{klasses.join(', ')}.\nMaybe try explicitly setting the type using :with parameter. Valid options are: #{source_types.keys.join(', ')}")
|
261
|
+
end
|
262
|
+
return klasses.first
|
263
|
+
end
|
264
|
+
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'fpm/fry/source'
|
2
|
+
require 'fpm/fry/source/package'
|
3
|
+
require 'fpm/fry/source/dir'
|
4
|
+
require 'fpm/fry/source/patched'
|
5
|
+
require 'fpm/fry/source/git'
|
6
|
+
require 'fpm/fry/plugin'
|
7
|
+
require 'fpm/fry/os_db'
|
8
|
+
require 'shellwords'
|
9
|
+
require 'cabin'
|
10
|
+
require 'open3'
|
11
|
+
module FPM; module Fry
|
12
|
+
|
13
|
+
class Recipe
|
14
|
+
|
15
|
+
class PackageRecipe
|
16
|
+
attr_accessor :name,
|
17
|
+
:iteration,
|
18
|
+
:version,
|
19
|
+
:maintainer,
|
20
|
+
:vendor,
|
21
|
+
:depends,
|
22
|
+
:provides,
|
23
|
+
:conflicts,
|
24
|
+
:replaces,
|
25
|
+
:suggests,
|
26
|
+
:recommends,
|
27
|
+
:scripts,
|
28
|
+
:output_hooks,
|
29
|
+
:files
|
30
|
+
|
31
|
+
|
32
|
+
def initialize
|
33
|
+
@name = nil
|
34
|
+
@iteration = nil
|
35
|
+
@version = '0.0.0'
|
36
|
+
@maintainer = nil
|
37
|
+
@vendor = nil
|
38
|
+
@depends = {}
|
39
|
+
@provides = {}
|
40
|
+
@conflicts = {}
|
41
|
+
@replaces = {}
|
42
|
+
@scripts = {
|
43
|
+
before_install: [],
|
44
|
+
after_install: [],
|
45
|
+
before_remove: [],
|
46
|
+
after_remove: []
|
47
|
+
}
|
48
|
+
@output_hooks = []
|
49
|
+
@files = []
|
50
|
+
end
|
51
|
+
|
52
|
+
alias dependencies depends
|
53
|
+
|
54
|
+
def apply_output( package )
|
55
|
+
package.name = name
|
56
|
+
package.version = version
|
57
|
+
package.iteration = iteration
|
58
|
+
package.maintainer = maintainer if maintainer
|
59
|
+
package.vendor = vendor if vendor
|
60
|
+
scripts.each do |type, scripts|
|
61
|
+
package.scripts[type] = scripts.join("\n") if scripts.any?
|
62
|
+
end
|
63
|
+
[:dependencies, :conflicts, :replaces, :provides].each do |sym|
|
64
|
+
send(sym).each do |name, options|
|
65
|
+
package.send(sym) << "#{name}#{options[:version]}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
output_hooks.each{|h| h.call(self, package) }
|
69
|
+
return package
|
70
|
+
end
|
71
|
+
|
72
|
+
alias apply apply_output
|
73
|
+
|
74
|
+
SYNTAX_CHECK_SHELLS = ['/bin/sh','/bin/bash', '/bin/dash']
|
75
|
+
|
76
|
+
def lint
|
77
|
+
problems = []
|
78
|
+
problems << "Name is empty." if name.to_s == ''
|
79
|
+
scripts.each do |type,scripts|
|
80
|
+
next if scripts.none?
|
81
|
+
s = scripts.join("\n")
|
82
|
+
if s == ''
|
83
|
+
problems << "#{type} script is empty. This will produce broken packages."
|
84
|
+
next
|
85
|
+
end
|
86
|
+
m = /\A#!([^\n]+)\n/.match(s)
|
87
|
+
if !m
|
88
|
+
problems << "#{type} script doesn't have a valid shebang"
|
89
|
+
next
|
90
|
+
end
|
91
|
+
begin
|
92
|
+
args = m[1].shellsplit
|
93
|
+
rescue ArgumentError => e
|
94
|
+
problems << "#{type} script doesn't have a valid command in shebang"
|
95
|
+
end
|
96
|
+
if SYNTAX_CHECK_SHELLS.include? args[0]
|
97
|
+
sin, sout, serr, th = Open3.popen3(args[0],'-n')
|
98
|
+
sin.write(s)
|
99
|
+
sin.close
|
100
|
+
if th.value.exitstatus != 0
|
101
|
+
problems << "#{type} script is not valid #{args[0]} code: #{serr.read.chomp}"
|
102
|
+
end
|
103
|
+
serr.close
|
104
|
+
sout.close
|
105
|
+
end
|
106
|
+
end
|
107
|
+
return problems
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
attr_accessor :source, :steps, :packages, :build_depends, :input_hooks
|
112
|
+
|
113
|
+
def initialize
|
114
|
+
@source = Source::Null
|
115
|
+
@steps = {}
|
116
|
+
@packages = [PackageRecipe.new]
|
117
|
+
@packages[0].files << '**'
|
118
|
+
@build_depends = {}
|
119
|
+
@input_hooks = []
|
120
|
+
end
|
121
|
+
|
122
|
+
def depends
|
123
|
+
depends = @packages.map(&:depends).inject(:merge)
|
124
|
+
@packages.map(&:name).each do | n |
|
125
|
+
depends.delete(n)
|
126
|
+
end
|
127
|
+
return depends
|
128
|
+
end
|
129
|
+
|
130
|
+
def lint
|
131
|
+
packages.flat_map(&:lint)
|
132
|
+
end
|
133
|
+
|
134
|
+
def apply_input( package )
|
135
|
+
input_hooks.each{|h| h.call(self, package) }
|
136
|
+
return package
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
end ; end
|