Module: Everywhere::Platform::Snapshot

Defined in:
lib/everywhere/platform/snapshot.rb

Overview

Packs the app's source into a .tar.gz for a Platform build, honoring .everywhereignore (so dist/, node_modules, .git, secrets never ship). Pure Ruby — no tar dependency — and streams file-by-file so a large tree doesn't blow up memory.

Class Method Summary collapse

Class Method Details

.add(tar, root, rel) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/everywhere/platform/snapshot.rb', line 40

def add(tar, root, rel)
  abs = File.join(root, rel)
  # lstat, and never follow: File.stat would read through the link, so a
  # dangling one (a deleted bundle path, a checked-in link to an absent
  # sibling repo) raises Errno::ENOENT and aborts the whole snapshot —
  # and a LIVE one pointing outside the tree (link → ~/.ssh/id_rsa) would
  # pack and upload its target's contents under an innocent name.
  return Everywhere::UI.warn("skipping symlink #{rel}") if File.lstat(abs).symlink?

  stat = File.stat(abs)
  tar.add_file_simple(rel, stat.mode & 0o777, stat.size) do |io|
    File.open(abs, "rb") { |f| IO.copy_stream(f, io) }
  end
rescue Errno::ENOENT, Errno::EACCES => e
  # The listing is a snapshot in time; a dev server or watcher can delete a
  # tmp/ file between the glob and here. Losing it beats losing the build.
  Everywhere::UI.warn("skipping #{rel} (#{e.class.name.split("::").last})")
end

.create(root, out_path) ⇒ Object

Writes root's non-ignored files to out_path (a .tar.gz). Returns the file count.



20
21
22
23
24
25
26
27
28
29
30
# File 'lib/everywhere/platform/snapshot.rb', line 20

def create(root, out_path)
  files = Everywhere::Ignore.for(root).files(root)
  File.open(out_path, "wb") do |dest|
    Zlib::GzipWriter.wrap(dest) do |gz|
      Gem::Package::TarWriter.new(gz) do |tar|
        files.each { |rel| add(tar, root, rel) }
      end
    end
  end
  files.size
end

.md5_base64(path) ⇒ Object

base64 MD5 of a file (what Active Storage's direct upload verifies), computed in chunks so large snapshots don't load into memory.



34
35
36
37
38
# File 'lib/everywhere/platform/snapshot.rb', line 34

def md5_base64(path)
  digest = Digest::MD5.new
  File.open(path, "rb") { |f| digest << f.read(1 << 20) until f.eof? }
  digest.base64digest
end