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

#build_dirObject

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



239
240
241
# File 'lib/pkgr/builder.rb', line 239

def build_dir
  @build_dir ||= Dir.mktmpdir
end

#buildpack_for_appObject

Buildpack detected for the app, if any.



277
278
279
280
281
282
283
# File 'lib/pkgr/builder.rb', line 277

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.



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

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
# File 'lib/pkgr/builder.rb', line 22

def call
  extract
  update_config
  check
  setup
  setup_pipeline
  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



87
88
89
90
# File 'lib/pkgr/builder.rb', line 87

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

#compileObject

Pass the app through the buildpack



108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/pkgr/builder.rb', line 108

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.



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

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

#config_fileObject



229
230
231
# File 'lib/pkgr/builder.rb', line 229

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

#distributionObject

Returns the current distribution we’re packaging for.



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

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

#extractObject

Extract the given tarball to the target directory



39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/pkgr/builder.rb', line 39

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



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

def fpm_command
  distribution.fpm_command(build_dir)
end

#package(remaining_attempts = 3) ⇒ Object

Launch the FPM command that will generate the package.



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/pkgr/builder.rb', line 171

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



76
77
78
79
80
81
82
83
84
# File 'lib/pkgr/builder.rb', line 76

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.



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

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

#procfileObject

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



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

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

#procfile_entriesObject



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/pkgr/builder.rb', line 204

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.



225
226
227
# File 'lib/pkgr/builder.rb', line 225

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

#scaling_dirObject



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

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

#setupObject

Setup the build directory structure



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

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

#setup_cronsObject

Write cron files



157
158
159
160
161
162
163
164
165
166
167
# File 'lib/pkgr/builder.rb', line 157

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



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

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

#source_dirObject

Path to the directory containing the main app files.



234
235
236
# File 'lib/pkgr/builder.rb', line 234

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

#store_cacheObject



192
193
194
195
196
197
# File 'lib/pkgr/builder.rb', line 192

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



200
201
202
# File 'lib/pkgr/builder.rb', line 200

def teardown
  FileUtils.rm_rf(build_dir)
end

#update_configObject

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



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/pkgr/builder.rb', line 55

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
  # useful for templates that need to read files
  config.source_dir = source_dir
  config.build_dir = build_dir
end

#vendor_dirObject



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

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

#verifyObject



187
188
189
190
# File 'lib/pkgr/builder.rb', line 187

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.



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

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.



144
145
146
147
148
149
150
151
152
153
154
# File 'lib/pkgr/builder.rb', line 144

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