Class: Foobara::AWS::Packager

Inherits:
Object
  • Object
show all
Defined in:
lib/foobara/aws/packager.rb

Overview

Builds one deployable artifact per unit in a Plan.

BUILD time — neither runtime nor synth. It shells out to Docker, so it is required explicitly and never from a Lambda or a CDK app.

Foobara::AWS::Packager.new(plan:, root: ".", out: "build").build

Each artifact contains the application's sources, a generated handler.rb, and a standalone gem bundle holding only that unit's dependencies. Units whose gemfiles resolve identically share one bundle build.

It also writes the plan next to them. That is the contract with synthesis: the CDK app reads what was BUILT, so it cannot route to an artifact that does not exist, and it needs neither a running connector nor the app.

Constant Summary collapse

DEFAULT_IMAGE =

A Lambda-like container, so native extensions are built against the runtime's own libraries rather than the host's.

"public.ecr.aws/sam/build-ruby4.0"
BUNDLE_COMMAND =

Bundler's standalone mode, NOT bundle exec: Bundler's runtime costs several hundred milliseconds at Lambda's smaller memory sizes, on every cold start, for nothing a deployed artifact needs.

"bundle install --standalone"

Instance Method Summary collapse

Constructor Details

#initialize(plan:, root: Dir.pwd, out: "build", sources: %w[app config],, gemfiles: "units", image: DEFAULT_IMAGE, env: {}, mounts: [], handler_template: nil, authorizer: nil, docker: nil) ⇒ Packager

rubocop:disable Metrics/ParameterLists



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/foobara/aws/packager.rb', line 38

def initialize(plan:, root: Dir.pwd, out: "build", sources: %w[app config],
               gemfiles: "units", image: DEFAULT_IMAGE, env: {}, mounts: [],
               handler_template: nil, authorizer: nil, docker: nil)
  @plan = plan
  @root = File.expand_path(root)
  @out = File.expand_path(out, @root)
  @sources = sources
  @gemfiles = gemfiles
  @image = image
  @env = env
  # Extra host paths to mount. A gemfile referencing a gem by path outside
  # the application root would otherwise fail inside the container, which
  # sees only the root.
  @mounts = mounts
  @handler_template = handler_template
  @authorizer = authorizer
  # Injectable so the layout can be tested without Docker, and so an
  # environment with a different container runtime can substitute one.
  @docker = docker || method(:run_docker)
end

Instance Method Details

#buildObject

rubocop:enable Metrics/ParameterLists



60
61
62
63
64
65
66
67
68
# File 'lib/foobara/aws/packager.rb', line 60

def build
  FileUtils.mkdir_p(@out)

  built = @plan.units.map { |unit| build_unit(unit) }
  built << build_authorizer if @authorizer

  File.write(plan_path, JSON.pretty_generate(@plan.to_h))
  built
end

#plan_pathObject



70
# File 'lib/foobara/aws/packager.rb', line 70

def plan_path = File.join(@out, "plan.json")