litbuild 1.0.1
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.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +1 -0
- data/LICENSE +14 -0
- data/README +135 -0
- data/bin/lb +69 -0
- data/gpl-3.0.txt +674 -0
- data/lib/litbuild/ascii_doc_visitor.rb +557 -0
- data/lib/litbuild/bash_script_visitor.rb +547 -0
- data/lib/litbuild/blueprint.rb +232 -0
- data/lib/litbuild/blueprint_library.rb +122 -0
- data/lib/litbuild/blueprint_parser.rb +210 -0
- data/lib/litbuild/commands.rb +37 -0
- data/lib/litbuild/driver.rb +66 -0
- data/lib/litbuild/errors.rb +39 -0
- data/lib/litbuild/logfile_namer.rb +23 -0
- data/lib/litbuild/multi_part_visitor.rb +35 -0
- data/lib/litbuild/narrative.rb +16 -0
- data/lib/litbuild/package.rb +143 -0
- data/lib/litbuild/section.rb +73 -0
- data/lib/litbuild/service_dir.rb +59 -0
- data/lib/litbuild/source_code_manager.rb +143 -0
- data/lib/litbuild/source_files_visitor.rb +26 -0
- data/lib/litbuild/url_visitor.rb +29 -0
- data/lib/litbuild/version.rb +5 -0
- data/lib/litbuild/visitor.rb +42 -0
- data/lib/litbuild.rb +16 -0
- data.tar.gz.sig +0 -0
- metadata +94 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,547 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'litbuild/service_dir'
|
4
|
+
require 'litbuild/source_code_manager'
|
5
|
+
require 'litbuild/visitor'
|
6
|
+
|
7
|
+
module Litbuild
|
8
|
+
##
|
9
|
+
# This class writes bash scripts to directories, rooted at the
|
10
|
+
# SCRIPT_DIR directory. It adds a sequential number prefix on each
|
11
|
+
# script written to a directory, and ensures that no duplicate scripts
|
12
|
+
# are written: if a script for a specific blueprint (or blueprint
|
13
|
+
# phase) has already been written to any directory, BashScriptVisitor
|
14
|
+
# will ignore subsequent requests to write that blueprint (phase).
|
15
|
+
class BashScriptVisitor < Visitor
|
16
|
+
INSTALL_GID = 9999
|
17
|
+
|
18
|
+
attr_reader :blueprint_dir
|
19
|
+
|
20
|
+
def initialize(parameters:)
|
21
|
+
@parameters = parameters
|
22
|
+
super(directory: @parameters['SCRIPT_DIR'])
|
23
|
+
@written = Hash.new { |hash, key| hash[key] = [] }
|
24
|
+
@all_written_targets = []
|
25
|
+
@all_commands = []
|
26
|
+
@blueprint_dir = quote(File.expand_path('.'))
|
27
|
+
@scm = SourceCodeManager.new(@parameters['TARFILE_DIR'],
|
28
|
+
@parameters['PATCH_DIR'])
|
29
|
+
end
|
30
|
+
|
31
|
+
def visit_commands(commands:)
|
32
|
+
write(blueprint: commands, location: cwd) do |script|
|
33
|
+
phase = commands.active_phase
|
34
|
+
restart_file = if phase
|
35
|
+
"#{commands.name}::#{phase.tr(' ', '_')}"
|
36
|
+
else
|
37
|
+
commands.name
|
38
|
+
end
|
39
|
+
render_restart_header(script, restart_file)
|
40
|
+
log = commands.logfile(commands.name, phase)
|
41
|
+
cmds = commands['commands'] || []
|
42
|
+
files = handle_file_directives(commands)
|
43
|
+
render_servicedirs(script: script,
|
44
|
+
dirs: commands['servicedir'],
|
45
|
+
pipelines: commands['service-pipeline'])
|
46
|
+
cmds = [files, cmds].flatten
|
47
|
+
cmds.each do |command|
|
48
|
+
render_command(script, command, log)
|
49
|
+
end
|
50
|
+
render_cfgrepo_trailer(script, commands, log)
|
51
|
+
render_restart_trailer(script, restart_file)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def visit_package(package:)
|
56
|
+
write(blueprint: package, location: cwd) do |script|
|
57
|
+
if (File.stat('/etc').gid == INSTALL_GID) &&
|
58
|
+
Process.uid.zero? &&
|
59
|
+
(ENV['LITBUILD_PKGUSR'] != 'false')
|
60
|
+
pkgusr = { 'name' => [package.name],
|
61
|
+
'description' => package['full-name'] }
|
62
|
+
package.directives['package-user'] ||= [pkgusr]
|
63
|
+
end
|
64
|
+
if package.directives.include?('package-user')
|
65
|
+
render_package_user(package, script)
|
66
|
+
else
|
67
|
+
render_standard_package(package, script)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def visit_section(section:)
|
73
|
+
section_dir = File.join(cwd, section.name)
|
74
|
+
write(blueprint: section, location: cwd) do |script|
|
75
|
+
render_restart_header(script, section.name)
|
76
|
+
script.puts("cd $(dirname $0)/#{section.name}")
|
77
|
+
skip_line(script)
|
78
|
+
write_components(location: section_dir, script: script)
|
79
|
+
render_restart_trailer(script, section.name)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def write_sudoers
|
84
|
+
script_dir = @parameters['SCRIPT_DIR']
|
85
|
+
sudoers = sudoers_entries.sort
|
86
|
+
return if sudoers.empty?
|
87
|
+
|
88
|
+
full_path = File.join(script_dir, 'lb-sudoers')
|
89
|
+
File.open(full_path, 'w') do |f|
|
90
|
+
sudoers.each do |s|
|
91
|
+
f.puts(s)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def write_toplevel_script(target:)
|
97
|
+
script_dir = @parameters['SCRIPT_DIR']
|
98
|
+
return if @written[script_dir].empty?
|
99
|
+
|
100
|
+
script_name = File.join(script_dir, "#{target}.sh")
|
101
|
+
File.open(script_name, 'w') do |f|
|
102
|
+
f.puts("#!#{find_bash}/bash")
|
103
|
+
skip_line(f)
|
104
|
+
f.puts("trap 'echo UTTER FAILURE on line $LINENO' ERR")
|
105
|
+
f.puts('set -e -v')
|
106
|
+
skip_line(f)
|
107
|
+
write_components(location: script_dir, script: f)
|
108
|
+
f.puts('set +v')
|
109
|
+
f.puts('echo TOTAL SUCCESS')
|
110
|
+
end
|
111
|
+
FileUtils.chmod('ugo+x', script_name)
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def write(blueprint:, location:, &block)
|
117
|
+
return if @all_written_targets.include?(blueprint.target_name)
|
118
|
+
|
119
|
+
FileUtils.mkdir_p(location)
|
120
|
+
script_name = format('%<count>02d-%<script>s',
|
121
|
+
count: @written[location].size,
|
122
|
+
script: blueprint.file_name + '.sh')
|
123
|
+
script_path = File.join(location, script_name)
|
124
|
+
File.open(script_path, 'w') do |f|
|
125
|
+
f.write(to_bash_script(blueprint, block))
|
126
|
+
end
|
127
|
+
FileUtils.chmod('ugo+x', script_path)
|
128
|
+
envcmds = environment_commands(blueprint).reject { |c| c =~ /^mkdir/ }
|
129
|
+
unless envcmds.empty?
|
130
|
+
envscript = File.join(location, "#{blueprint.file_name}_env.sh")
|
131
|
+
File.open(envscript, 'w') do |f|
|
132
|
+
envcmds.each { |cmd| f.puts(cmd) }
|
133
|
+
end
|
134
|
+
FileUtils.chmod('ugo+x', envscript)
|
135
|
+
end
|
136
|
+
@written[location] << script_name
|
137
|
+
@all_written_targets << blueprint.target_name
|
138
|
+
end
|
139
|
+
|
140
|
+
def to_bash_script(blueprint, block)
|
141
|
+
if blueprint.phases? && !blueprint.active_phase
|
142
|
+
raise(ParameterMissing,
|
143
|
+
"Phase must be set to render script for #{blueprint.name}")
|
144
|
+
end
|
145
|
+
|
146
|
+
script = StringIO.new
|
147
|
+
script.puts("#!#{find_bash}/bash")
|
148
|
+
skip_line(script)
|
149
|
+
script.puts("export LB_BLUEPRINT_DIR=#{blueprint_dir}")
|
150
|
+
script.puts("trap 'echo #{blueprint.failure_line} on line $LINENO' ERR")
|
151
|
+
script.puts('set -e -v')
|
152
|
+
skip_line(script)
|
153
|
+
env_cmds = environment_commands(blueprint)
|
154
|
+
unless env_cmds.empty?
|
155
|
+
env_cmds.each do |cmd|
|
156
|
+
script.puts(cmd)
|
157
|
+
end
|
158
|
+
skip_line(script)
|
159
|
+
end
|
160
|
+
block.call(script)
|
161
|
+
skip_line(script)
|
162
|
+
script.puts('set +v')
|
163
|
+
script.puts("echo #{blueprint.success_line}")
|
164
|
+
script.string
|
165
|
+
end
|
166
|
+
|
167
|
+
def write_components(location:, script:)
|
168
|
+
@written[location].map do |target|
|
169
|
+
script.puts("echo \"At $(date): Beginning #{target}:\"")
|
170
|
+
script.puts("./#{target}")
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def find_bash
|
175
|
+
ENV['PATH'].split(':').detect { |pe| File.exist?(File.join(pe, 'bash')) }
|
176
|
+
end
|
177
|
+
|
178
|
+
def handle_file_directives(blueprint)
|
179
|
+
blueprint.files.keys.sort.map do |name|
|
180
|
+
accum = StringIO.new
|
181
|
+
accum.puts("cat > #{name} <<'LBEOF'")
|
182
|
+
accum.puts(blueprint.files[name].string)
|
183
|
+
accum.puts('LBEOF')
|
184
|
+
accum.string
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def sudoers_entries
|
189
|
+
uid = `id -u`.strip
|
190
|
+
return [] if uid == '0'
|
191
|
+
|
192
|
+
raw_sudo_cmds = @all_commands.select do |c|
|
193
|
+
c =~ /sudo / && c.lines.size < 2
|
194
|
+
end.uniq
|
195
|
+
sudo_cmds = raw_sudo_cmds.map do |c|
|
196
|
+
command = c.sub(/.*sudo /, '')
|
197
|
+
unless command.start_with?('/')
|
198
|
+
base_command = command.sub(/ .*$/, '')
|
199
|
+
raise(RelativeSudo,
|
200
|
+
"Need absolute path for \"#{base_command}\" in \"#{c}\"")
|
201
|
+
end
|
202
|
+
sudoed_cmd = c.sub(/^.*sudo (.*)$/, '\\1')
|
203
|
+
sudoed_cmd = sudoed_cmd.sub(/;.*$/, '') if sudoed_cmd.match?(/;/)
|
204
|
+
sudoed_cmd.gsub(/([,:=\\])/, '\\\\\1')
|
205
|
+
end
|
206
|
+
username = `id -un`.strip
|
207
|
+
sudo_cmds.map do |c|
|
208
|
+
"#{username} ALL = NOPASSWD: #{c}"
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def render_standard_package(package, script)
|
213
|
+
script.puts("export LB_SOURCE_DIR=#{quote(source_dir(package))}")
|
214
|
+
phase = package.active_phase
|
215
|
+
restart_file = if phase
|
216
|
+
"#{package.name}::#{phase.tr(' ', '_')}"
|
217
|
+
else
|
218
|
+
package.name
|
219
|
+
end
|
220
|
+
render_restart_header(script, restart_file, package.version)
|
221
|
+
render_prepare_source(package, script)
|
222
|
+
build_location = dir_for_build(package)
|
223
|
+
|
224
|
+
if package.build_dir
|
225
|
+
render_command(script, "mkdir -p #{build_location}", '/dev/null')
|
226
|
+
skip_line(script)
|
227
|
+
end
|
228
|
+
|
229
|
+
Package::BUILD_STAGES.each do |stage|
|
230
|
+
log = package.logfile(stage, phase)
|
231
|
+
render_in_dir(script, build_location) do
|
232
|
+
package.build_commands(stage).each do |command|
|
233
|
+
render_command(script, command, log)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
render_restart_trailer(script, restart_file, package.version)
|
238
|
+
end
|
239
|
+
|
240
|
+
def dir_for_build(package)
|
241
|
+
if (dir = package.build_dir)
|
242
|
+
File.expand_path(File.join(source_dir(package), dir))
|
243
|
+
else
|
244
|
+
source_dir(package)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def render_prepare_source(package, script)
|
249
|
+
log = package.logfile('prepare')
|
250
|
+
render_command(script, "if [ ! -d #{source_dir(package)} ]; then", log)
|
251
|
+
render_command(script, "mkdir -p #{work_site}", log)
|
252
|
+
render_in_dir(script, work_site) do
|
253
|
+
render_command(script, @scm.untar_command_for(package), log)
|
254
|
+
end
|
255
|
+
render_in_tree_sources(package, script, log)
|
256
|
+
render_patch_commands(package, script, log)
|
257
|
+
render_command(script, 'fi', log)
|
258
|
+
end
|
259
|
+
|
260
|
+
def work_site
|
261
|
+
@parameters['WORK_SITE']
|
262
|
+
end
|
263
|
+
|
264
|
+
def source_dir(package)
|
265
|
+
File.join(work_site, package.name_and_version)
|
266
|
+
end
|
267
|
+
|
268
|
+
def render_in_tree_sources(package, script, log)
|
269
|
+
intree_commands = @scm.intree_untar_commands_for(package)
|
270
|
+
return if intree_commands.empty?
|
271
|
+
|
272
|
+
render_in_dir(script, source_dir(package)) do
|
273
|
+
intree_commands.each do |cmd|
|
274
|
+
render_command(script, cmd, log)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
def render_patch_commands(package, script, log)
|
280
|
+
patch_commands = @scm.patch_commands_for(package)
|
281
|
+
return if patch_commands.empty?
|
282
|
+
|
283
|
+
render_in_dir(script, source_dir(package)) do
|
284
|
+
patch_commands.each do |command|
|
285
|
+
render_command(script, command, log)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def render_package_user(package, script)
|
291
|
+
pkgusr = package.pkgusr_name
|
292
|
+
log = package.logfile('pkgusr')
|
293
|
+
pkgusr_dir = "~#{pkgusr}"
|
294
|
+
package.directives['configuration-files'] ||= []
|
295
|
+
package.directives['configuration-files'] << "~#{pkgusr}/options"
|
296
|
+
restart_file = ".#{package.active_phase || 'default'}"
|
297
|
+
render_restart_header(script, restart_file, package.version, pkgusr_dir)
|
298
|
+
pkgusr_srcdir = File.join(pkgusr_dir, package.name_and_version)
|
299
|
+
render_add_package_user(package, script, log)
|
300
|
+
@scm.copy_source_files_commands(package).each do |cp_command|
|
301
|
+
render_command(script, cp_command, log)
|
302
|
+
end
|
303
|
+
script.puts("export LB_SOURCE_DIR=#{quote(pkgusr_srcdir)}")
|
304
|
+
skip_line(script)
|
305
|
+
render_command(script, generate_options_file(package, pkgusr_srcdir), log)
|
306
|
+
skip_line(script)
|
307
|
+
pre_build(package, script, log)
|
308
|
+
render_command(script,
|
309
|
+
"su -l -c /usr/libexec/pkgusr/build #{pkgusr}",
|
310
|
+
log)
|
311
|
+
post_build(package, script, log)
|
312
|
+
render_restart_trailer(script, restart_file, package.version, pkgusr_dir)
|
313
|
+
end
|
314
|
+
|
315
|
+
def pre_build(package, script, log)
|
316
|
+
render_servicedirs(script: script,
|
317
|
+
dirs: package['servicedir'],
|
318
|
+
pipelines: package['service-pipeline'])
|
319
|
+
return unless (bbar = package['before-build-as-root'])
|
320
|
+
|
321
|
+
bbar.each { |cmd| render_command(script, cmd, log) }
|
322
|
+
end
|
323
|
+
|
324
|
+
def post_build(package, script, log)
|
325
|
+
if (aiar = package['after-install-as-root'])
|
326
|
+
aiar.each { |cmd| render_command(script, cmd, log) }
|
327
|
+
end
|
328
|
+
render_cfgrepo_trailer(script, package, log)
|
329
|
+
render_command(script, 'set_install_dirs', log)
|
330
|
+
render_command(script, 'ldconfig', log)
|
331
|
+
end
|
332
|
+
|
333
|
+
def render_add_package_user(package, script, log)
|
334
|
+
pkgusr = package.value('package-user')
|
335
|
+
desc = pkgusr['description'].first || package.value('full_name')
|
336
|
+
render_command(script,
|
337
|
+
"add_package_user '#{desc}' #{package.pkgusr_name}",
|
338
|
+
log)
|
339
|
+
end
|
340
|
+
|
341
|
+
def generate_options_file(package, srcdir)
|
342
|
+
options = StringIO.new
|
343
|
+
options.puts("cat > ~#{package.pkgusr_name}/options <<'LBEOF'")
|
344
|
+
options.puts("export version=#{package.version}")
|
345
|
+
options.puts("export LB_SOURCE_DIR=#{quote(srcdir)}")
|
346
|
+
environment_commands(package).each { |cmd| options.puts(cmd) }
|
347
|
+
package.build_dir && options.puts("export build_dir=#{package.build_dir}")
|
348
|
+
render_intree_commands(package, options)
|
349
|
+
Package::BUILD_STAGES.each do |stage|
|
350
|
+
options.puts("function #{stage}_commands()\n{")
|
351
|
+
cmds = package.build_commands(stage)
|
352
|
+
if cmds.empty?
|
353
|
+
options.puts(':')
|
354
|
+
else
|
355
|
+
cmds.each { |cmd| options.puts(cmd) }
|
356
|
+
end
|
357
|
+
options.puts('}')
|
358
|
+
end
|
359
|
+
render_patches(package, options)
|
360
|
+
options.puts('LBEOF')
|
361
|
+
options.string
|
362
|
+
end
|
363
|
+
|
364
|
+
def render_intree_commands(package, options)
|
365
|
+
return if package.in_tree.empty?
|
366
|
+
|
367
|
+
options.puts('declare -A in_tree')
|
368
|
+
package.in_tree.sort.each do |basename, version, path|
|
369
|
+
options.puts("in_tree[#{basename}-#{version}]=#{path}")
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
def render_patches(package, options)
|
374
|
+
return if package.patch_files.empty?
|
375
|
+
|
376
|
+
options.puts('declare -a patches')
|
377
|
+
package.patch_files.each_with_index do |name, idx|
|
378
|
+
options.puts("patches[#{idx}]=#{name}")
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
def render_restart_header(script,
|
383
|
+
filename,
|
384
|
+
content = 'COMPLETE',
|
385
|
+
restart_dir = '"$LITBUILDDBDIR"')
|
386
|
+
path = "#{restart_dir}/#{filename}"
|
387
|
+
script.puts("if [ -d #{restart_dir} -a -w #{restart_dir} ]")
|
388
|
+
script.puts('then')
|
389
|
+
script.puts("if [ -f #{path} ]")
|
390
|
+
script.puts('then')
|
391
|
+
script.puts("grep -q '^#{content}$' #{path} && exit 0")
|
392
|
+
script.puts('fi')
|
393
|
+
script.puts('fi')
|
394
|
+
skip_line(script)
|
395
|
+
end
|
396
|
+
|
397
|
+
def render_restart_trailer(script,
|
398
|
+
filename,
|
399
|
+
content = 'COMPLETE',
|
400
|
+
restart_dir = '"$LITBUILDDBDIR"')
|
401
|
+
path = "#{restart_dir}/#{filename}"
|
402
|
+
skip_line(script)
|
403
|
+
script.puts("if [ -d #{restart_dir} -a -w #{restart_dir} ]")
|
404
|
+
script.puts('then')
|
405
|
+
script.puts("echo \"#{content}\" > #{path}")
|
406
|
+
script.puts('fi')
|
407
|
+
end
|
408
|
+
|
409
|
+
def render_command(script, command, log)
|
410
|
+
@all_commands << command
|
411
|
+
if command.match?(/>/)
|
412
|
+
# redirecting output of command, can't put stdout in log.
|
413
|
+
script.puts(command)
|
414
|
+
else
|
415
|
+
script.puts("#{command} >> #{log} 2>&1")
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
def render_in_dir(script, dir)
|
420
|
+
script.puts("pushd #{dir}")
|
421
|
+
yield
|
422
|
+
script.puts('popd')
|
423
|
+
end
|
424
|
+
|
425
|
+
def skip_line(script)
|
426
|
+
script.puts
|
427
|
+
end
|
428
|
+
|
429
|
+
def environment_commands(blueprint)
|
430
|
+
commands = []
|
431
|
+
if blueprint['environment']
|
432
|
+
env_directives = blueprint['environment']
|
433
|
+
merged_and_flattened = {}
|
434
|
+
env_directives.each do |env|
|
435
|
+
env.each { |key, val| merged_and_flattened[key] = val.first }
|
436
|
+
end
|
437
|
+
merged_and_flattened.each do |k, v|
|
438
|
+
commands << (v.empty? ? "unset #{k}" : "export #{k}=#{quote(v)}")
|
439
|
+
end
|
440
|
+
if merged_and_flattened.key?('LITBUILDDBDIR') &&
|
441
|
+
!merged_and_flattened['LITBUILDDBDIR'].empty?
|
442
|
+
commands << "mkdir -p #{merged_and_flattened['LITBUILDDBDIR']}"
|
443
|
+
end
|
444
|
+
end
|
445
|
+
commands
|
446
|
+
end
|
447
|
+
|
448
|
+
def render_servicedirs(script:, dirs:, pipelines:)
|
449
|
+
return unless dirs || pipelines
|
450
|
+
|
451
|
+
script.puts('pushd /etc/s6-rc/source')
|
452
|
+
skip_line(script)
|
453
|
+
pipelines&.each { |pipeline| render_service_pipeline(script, pipeline) }
|
454
|
+
dirs&.each { |sdir| render_service_dir(script, sdir) }
|
455
|
+
script.puts('popd')
|
456
|
+
skip_line(script)
|
457
|
+
end
|
458
|
+
|
459
|
+
def render_service_pipeline(script, spipe)
|
460
|
+
pname = spipe['name'].first
|
461
|
+
spipe['bundle']&.each do |bundle|
|
462
|
+
script.puts("grep -q '^#{pname}$' #{bundle}/contents || " \
|
463
|
+
"echo #{pname} >> #{bundle}/contents")
|
464
|
+
end
|
465
|
+
sdirs = spipe['servicedirs']
|
466
|
+
sdirs.each_with_index do |sdir, i|
|
467
|
+
render_service_dir(script, sdir)
|
468
|
+
if i == sdirs.size - 1
|
469
|
+
script.puts("echo #{pname} > #{sdir['name'].first}/pipeline-name")
|
470
|
+
end
|
471
|
+
if i < sdirs.size - 1
|
472
|
+
next_svc = sdirs[i + 1]['name'].first
|
473
|
+
script.puts("echo #{next_svc} > #{sdir['name'].first}/producer-for")
|
474
|
+
end
|
475
|
+
unless i.zero?
|
476
|
+
prev_svc = sdirs[i - 1]['name'].first
|
477
|
+
script.puts("echo #{prev_svc} > #{sdir['name'].first}/consumer-for")
|
478
|
+
end
|
479
|
+
skip_line(script)
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
def render_service_dir(script, sdir)
|
484
|
+
sd = ServiceDir.new(sdir)
|
485
|
+
if sd.bundle
|
486
|
+
script.puts("grep -q '^#{sd.name}$' #{sd.bundle}/contents || " \
|
487
|
+
"echo #{sd.name} >> #{sd.bundle}/contents")
|
488
|
+
end
|
489
|
+
script.puts("mkdir -p #{sd.name}")
|
490
|
+
sd.oneline_files.keys.sort.each do |fn|
|
491
|
+
script.puts("echo #{sd.oneline_files[fn]} > #{sd.name}/#{fn}")
|
492
|
+
end
|
493
|
+
multiline = sd.multiline_files
|
494
|
+
deps = sd.dependencies
|
495
|
+
multiline['dependencies'] = deps.join("\n") unless deps.empty?
|
496
|
+
multiline.keys.sort.each do |filename|
|
497
|
+
# I always terminate here documents in litbuild-generated
|
498
|
+
# scripts with "LBEOF", partly because I'm thinking "Litbuild
|
499
|
+
# End-of-File" but also partly because it makes me think of
|
500
|
+
# Shia LaBoeuf, and then I remember the Rob Cantor song of
|
501
|
+
# that name and giggle.
|
502
|
+
script.puts("cat > #{sd.name}/#{filename} <<'LBEOF'")
|
503
|
+
script.puts(multiline[filename])
|
504
|
+
script.puts('LBEOF')
|
505
|
+
end
|
506
|
+
env = sd.env
|
507
|
+
unless env.empty?
|
508
|
+
script.puts("mkdir -p #{sd.name}/env")
|
509
|
+
env.keys.sort.each do |envvar|
|
510
|
+
script.puts("echo '#{env[envvar]}' > #{sd.name}/env/#{envvar}")
|
511
|
+
end
|
512
|
+
end
|
513
|
+
skip_line(script)
|
514
|
+
end
|
515
|
+
|
516
|
+
def render_cfgrepo_trailer(script, blueprint, log)
|
517
|
+
cfgs = blueprint['configuration-files']
|
518
|
+
return unless cfgs
|
519
|
+
|
520
|
+
render_command(script, "cfggit add #{cfgs.join(' ')}", log)
|
521
|
+
render_command(script, 'cfggit stageall', log)
|
522
|
+
bp = "#{blueprint.class.name.split('::').last} #{blueprint.name}"
|
523
|
+
cmd = "cfggit as-default -m 'Configuration files for #{bp}'"
|
524
|
+
render_command(script, cmd, log)
|
525
|
+
end
|
526
|
+
|
527
|
+
# quote a string suitably for a bash script
|
528
|
+
def quote(value)
|
529
|
+
# if value contains embedded backslash and newline characters, get
|
530
|
+
# rid of those first.
|
531
|
+
oneline = value.gsub(/\\\n /m, '')
|
532
|
+
|
533
|
+
# now, if the value contains ' -- backslash-quote everything (incl spaces)
|
534
|
+
# if the value contains " -- single-quote the whole thing
|
535
|
+
# if the value contains other punctuation -- double-quote the whole thing
|
536
|
+
if oneline.match?(/'/)
|
537
|
+
oneline.gsub(/([\\ "'`$])/, '\\\\\1')
|
538
|
+
elsif oneline.match?(/"/)
|
539
|
+
"'#{oneline}'"
|
540
|
+
elsif oneline.match?(/[\\ `]/)
|
541
|
+
"\"#{oneline}\""
|
542
|
+
else
|
543
|
+
oneline
|
544
|
+
end
|
545
|
+
end
|
546
|
+
end
|
547
|
+
end
|