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.



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

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.



9
10
11
# File 'lib/pkgr/builder.rb', line 9

def config
  @config
end

#tarballObject (readonly)

Returns the value of attribute tarball.



9
10
11
# File 'lib/pkgr/builder.rb', line 9

def tarball
  @tarball
end

Instance Method Details

#build_dirObject

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



177
178
179
# File 'lib/pkgr/builder.rb', line 177

def build_dir
  @build_dir ||= Dir.mktmpdir
end

#buildpack_for_appObject

Buildpack detected for the app, if any.



215
216
217
218
219
220
221
# File 'lib/pkgr/builder.rb', line 215

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.



210
211
212
# File 'lib/pkgr/builder.rb', line 210

def buildpacks
  distribution.buildpacks(config)
end

#callObject

Launch the full packaging procedure



19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/pkgr/builder.rb', line 19

def call
  extract
  update_config
  check
  setup
  compile
  write_env
  write_init
  package
ensure
  teardown if config.clean
end

#checkObject

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



33
34
35
36
# File 'lib/pkgr/builder.rb', line 33

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

#compileObject

Pass the app through the buildpack



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

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.



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

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

#config_fileObject



167
168
169
# File 'lib/pkgr/builder.rb', line 167

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

#distributionObject

Returns the current distribution we’re packaging for.



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

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

#extractObject

Extract the given tarball to the target directory



50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/pkgr/builder.rb', line 50

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



223
224
225
# File 'lib/pkgr/builder.rb', line 223

def fpm_command
  distribution.fpm_command(build_dir, config)
end

#packageObject

Launch the FPM command that will generate the package.



130
131
132
133
134
135
# File 'lib/pkgr/builder.rb', line 130

def package
  app_package = Mixlib::ShellOut.new(fpm_command)
  app_package.logger = Pkgr.logger
  app_package.run_command
  app_package.error!
end

#proc_dirObject

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



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

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

#procfileObject

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



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

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

#procfile_entriesObject



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

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.



163
164
165
# File 'lib/pkgr/builder.rb', line 163

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

#scaling_dirObject



190
191
192
# File 'lib/pkgr/builder.rb', line 190

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

#setupObject

Setup the build directory structure



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

def setup
  Dir.chdir(build_dir) do
    # useful for templates that need to read files
    config.source_dir = source_dir
    distribution.templates(config).each do |template|
      template.install(config.sesame)
    end
  end
end

#source_dirObject

Path to the directory containing the main app files.



172
173
174
# File 'lib/pkgr/builder.rb', line 172

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

#teardownObject

Make sure to get rid of the build directory



138
139
140
# File 'lib/pkgr/builder.rb', line 138

def teardown
  FileUtils.rm_rf(build_dir)
end

#update_configObject

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



66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/pkgr/builder.rb', line 66

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}"

    # 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
end

#vendor_dirObject



181
182
183
# File 'lib/pkgr/builder.rb', line 181

def vendor_dir
  File.join(source_dir, "vendor", "pkgr")
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.



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/pkgr/builder.rb', line 99

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.



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

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