Class: Pkgr::Builder

Inherits:
Object
  • Object
show all
Defined in:
lib/pkgr/builder.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tarball, config) ⇒ Builder

Accepts a path to a tarball (gzipped or not), or you can pass ‘-’ to read from stdin.



15
16
17
18
19
# File 'lib/pkgr/builder.rb', line 15

def initialize(tarball, config)
  @tarball = tarball
  @config = config
  Pkgr.debug "Initializing builder with the following config: #{config.inspect}"
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



12
13
14
# File 'lib/pkgr/builder.rb', line 12

def config
  @config
end

#tarballObject (readonly)

Returns the value of attribute tarball.



12
13
14
# File 'lib/pkgr/builder.rb', line 12

def tarball
  @tarball
end

Instance Method Details

#addons_dirObject



266
267
268
# File 'lib/pkgr/builder.rb', line 266

def addons_dir
  File.join(vendor_dir, "addons")
end

#build_dirObject

Build directory. Will be used by fpm to make the package.



258
259
260
# File 'lib/pkgr/builder.rb', line 258

def build_dir
  @build_dir ||= Dir.mktmpdir
end

#buildpack_for_appObject

Buildpack detected for the app, if any.



300
301
302
303
304
305
306
# File 'lib/pkgr/builder.rb', line 300

def buildpack_for_app
  raise "#{source_dir} does not exist" unless File.directory?(source_dir)
  @buildpack_for_app ||= buildpacks.find do |buildpack|
    buildpack.setup(config.edge, config.home)
    buildpack.detect(source_dir)
  end
end

#buildpacksObject

List of available buildpacks for the current distribution.



295
296
297
# File 'lib/pkgr/builder.rb', line 295

def buildpacks
  distribution.buildpacks
end

#callObject

Launch the full packaging procedure



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/pkgr/builder.rb', line 22

def call
  extract
  update_config
  check
  setup

  if config.installer
    setup_pipeline
  else
    setup_addons
  end

  compile
  write_env
  write_init
  setup_crons
  package
  store_cache
ensure
  teardown if config.clean
end

#checkObject

Check configuration, and verifies that the current distribution’s requirements are satisfied



95
96
97
98
# File 'lib/pkgr/builder.rb', line 95

def check
  raise Errors::ConfigurationInvalid, config.errors.join("; ") unless config.valid?
  distribution.check
end

#compileObject

Pass the app through the buildpack



127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/pkgr/builder.rb', line 127

def compile
  if buildpack_for_app
    puts "-----> #{buildpack_for_app.banner} app"

    FileUtils.mkdir_p(compile_cache_dir)

    run_hook config.before_hook
    buildpack_for_app.compile(source_dir, compile_cache_dir)
    buildpack_for_app.release(source_dir, compile_cache_dir)
    run_hook config.after_hook
  else
    raise Errors::UnknownAppType, "Can't find a buildpack for your app"
  end
end

#compile_cache_dirObject

Directory where the buildpacks can store stuff.



285
286
287
# File 'lib/pkgr/builder.rb', line 285

def compile_cache_dir
  config.compile_cache_dir || File.join(source_dir, ".git/cache")
end

#config_fileObject



248
249
250
# File 'lib/pkgr/builder.rb', line 248

def config_file
  File.join(source_dir, ".pkgr.yml")
end

#distributionObject

Returns the current distribution we’re packaging for.



290
291
292
# File 'lib/pkgr/builder.rb', line 290

def distribution
  @distribution ||= Distributions.current(config)
end

#extractObject

Extract the given tarball to the target directory



45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/pkgr/builder.rb', line 45

def extract
  FileUtils.mkdir_p source_dir

  opts = {}
  if tarball == "-"
    # FIXME: not really happy with reading everything in memory
    opts[:input] = $stdin.read
  end

  tarball_extract = Mixlib::ShellOut.new("tar xzf #{tarball} -C #{source_dir}", opts)
  tarball_extract.logger = Pkgr.logger
  tarball_extract.run_command
  tarball_extract.error!
end

#fpm_commandObject



308
309
310
# File 'lib/pkgr/builder.rb', line 308

def fpm_command
  distribution.fpm_command(build_dir)
end

#package(remaining_attempts = 3) ⇒ Object

Launch the FPM command that will generate the package.



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/pkgr/builder.rb', line 190

def package(remaining_attempts = 3)
  app_package = Mixlib::ShellOut.new(fpm_command)
  app_package.logger = Pkgr.logger
  app_package.run_command
  app_package.error!
  begin
    verify
  rescue Mixlib::ShellOut::ShellCommandFailed => e
    if remaining_attempts > 0
      package(remaining_attempts - 1)
    else
      raise
    end
  end
end

#pipelineObject



84
85
86
87
88
89
90
91
92
# File 'lib/pkgr/builder.rb', line 84

def pipeline
  @pipeline ||= begin
    components = []
    unless config.wizards.empty? || config.installer == false
      components << Installer.new(config.installer, distribution).setup
    end
    components
  end
end

#proc_dirObject

Directory where binstubs will be created for the corresponding Procfile commands.



271
272
273
# File 'lib/pkgr/builder.rb', line 271

def proc_dir
  File.join(vendor_dir, "processes")
end

#procfileObject

Returns the path to the app’s (supposedly present) Procfile.



280
281
282
# File 'lib/pkgr/builder.rb', line 280

def procfile
  File.join(source_dir, "Procfile")
end

#procfile_entriesObject



223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/pkgr/builder.rb', line 223

def procfile_entries
  @procfile_entries ||= begin
    default_process_types = YAML.load_file(release_file)["default_process_types"]

    default_process_types = {} unless default_process_types

    entries = if File.exist?(procfile)
      File.read(procfile).gsub("\r\n","\n").split("\n").map do |line|
        if line =~ /^([A-Za-z0-9_]+):\s*(.+)$/
          [$1, $2]
        end
      end.compact
    else
      []
    end

    default_process_types.merge(Hash[entries]).map{|name, command| Process.new(name, command)}
  end
end

#release_fileObject

Path to the release file generated after the buildpack compilation.



244
245
246
# File 'lib/pkgr/builder.rb', line 244

def release_file
  File.join(source_dir, ".release")
end

#scaling_dirObject



275
276
277
# File 'lib/pkgr/builder.rb', line 275

def scaling_dir
  File.join(vendor_dir, "scaling")
end

#setupObject

Setup the build directory structure



101
102
103
104
105
106
107
# File 'lib/pkgr/builder.rb', line 101

def setup
  Dir.chdir(build_dir) do
    distribution.templates.each do |template|
      template.install(config.sesame)
    end
  end
end

#setup_addonsObject

LEGACY, remove once openproject no longer needs it If addons are declared in .pkgr.yml, add them



117
118
119
120
121
122
123
124
# File 'lib/pkgr/builder.rb', line 117

def setup_addons
  config.addons.each do |addon|
    puts "-----> [addon] #{addon.name} (#{addon.url} @ #{addon.branch})"
    addon.install!(source_dir)
    dependency = distribution.add_addon(addon)
    config.dependencies.push(dependency) if dependency
  end
end

#setup_cronsObject

Write cron files



176
177
178
179
180
181
182
183
184
185
186
# File 'lib/pkgr/builder.rb', line 176

def setup_crons
  crons_dir = File.join("/", distribution.crons_dir)

  config.crons.map! do |cron_path|
    Cron.new(File.expand_path(cron_path, config.home), File.join(crons_dir, File.basename(cron_path)))
  end

  config.crons.each do |cron|
    puts "-----> [cron] #{cron.source} => #{cron.destination}"
  end
end

#setup_pipelineObject



109
110
111
112
113
# File 'lib/pkgr/builder.rb', line 109

def setup_pipeline
  pipeline.each do |component|
    @config = component.call(config)
  end
end

#source_dirObject

Path to the directory containing the main app files.



253
254
255
# File 'lib/pkgr/builder.rb', line 253

def source_dir
  File.join(build_dir, config.home)
end

#store_cacheObject



211
212
213
214
215
216
# File 'lib/pkgr/builder.rb', line 211

def store_cache
  return true unless config.store_cache
  generate_cache_tarball = Mixlib::ShellOut.new %{tar czf cache.tar.gz -C #{compile_cache_dir} .}
  generate_cache_tarball.logger = Pkgr.logger
  generate_cache_tarball.run_command
end

#teardownObject

Make sure to get rid of the build directory



219
220
221
# File 'lib/pkgr/builder.rb', line 219

def teardown
  FileUtils.rm_rf(build_dir)
end

#update_configObject

Update existing config with the one from .pkgr.yml file, if any



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/pkgr/builder.rb', line 61

def update_config
  if File.exist?(config_file)
    Pkgr.debug "Loading #{distribution.slug} from #{config_file}."
    @config = Config.load_file(config_file, distribution.slug).merge(config)
    Pkgr.debug "Found .pkgr.yml file. Updated config is now: #{config.inspect}"

    # update distribution config
    distribution.config = @config

    # FIXME: make Config the authoritative source of the runner config (distribution only tells the default runner)
    if @config.runner
      type, *version = @config.runner.split("-")
      distribution.runner = Distributions::Runner.new(type, version.join("-"))
    end
  end
  config.distribution = distribution
  # required to build proper Addon objects
  config.addons_dir = addons_dir
  # useful for templates that need to read files
  config.source_dir = source_dir
  config.build_dir = build_dir
end

#vendor_dirObject



262
263
264
# File 'lib/pkgr/builder.rb', line 262

def vendor_dir
  File.join(source_dir, "vendor", "pkgr")
end

#verifyObject



206
207
208
209
# File 'lib/pkgr/builder.rb', line 206

def verify
  return true unless config.verify
  distribution.verify(Dir.pwd)
end

#write_envObject

Parses the output of buildpack/bin/release executable to find out its default Procfile commands. Then merges those with the ones from the app’s Procfile (if any). Finally, generates a binstub in vendor/pkgr/processes/ so that these commands can be called using the app’s executable.



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/pkgr/builder.rb', line 145

def write_env
  FileUtils.mkdir_p proc_dir

  procfile_entries.each do |process|
    process_file = File.join(proc_dir, process.name)

    File.open(process_file, "w+") do |f|
      f.puts "#!/bin/sh"
      f << "exec "
      f << process.command
      f << " $@"
    end

    FileUtils.chmod 0755, process_file
  end
end

#write_initObject

Write startup scripts.



163
164
165
166
167
168
169
170
171
172
173
# File 'lib/pkgr/builder.rb', line 163

def write_init
  FileUtils.mkdir_p scaling_dir
  Dir.chdir(scaling_dir) do
    distribution.initializers_for(config.name, procfile_entries).each do |(process, file)|
      process_config = config.dup
      process_config.process_name = process.name
      process_config.process_command = process.command
      file.install(process_config.sesame)
    end
  end
end