Module: Clacky::ExtensionPackager

Defined in:
lib/clacky/extension/packager.rb

Overview

Packs a local extension container into a distributable zip, and installs a zip (local path or URL) into the installed layer.

These are the local halves of the publish/marketplace flow: pack produces the artifact that later gets uploaded, install is what a download performs. Both are pure filesystem operations with no network dependency beyond the optional URL fetch in install.

Defined Under Namespace

Classes: Error, Result

Constant Summary collapse

MANIFEST =
"ext.yml"
MAX_ZIP_SIZE =
50 * 1024 * 1024

Class Method Summary collapse

Class Method Details

.install(source, installed_dir: Clacky::ExtensionLoader::INSTALLED_DIR, force: false) ⇒ Object

Install a packed container into the installed layer. source is a local zip path or an http(s) URL. Validates the archive (single top container with an ext.yml, no path traversal), extracts it, then runs verify on the resolved installed layer.



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/clacky/extension/packager.rb', line 54

def install(source, installed_dir: Clacky::ExtensionLoader::INSTALLED_DIR, force: false)
  Dir.mktmpdir("clacky-ext-install") do |tmp|
    zip_path = local_zip_for(source, tmp)
    extract_root = File.join(tmp, "unpacked")
    FileUtils.mkdir_p(extract_root)

    extract_zip(zip_path, extract_root)

    ext_id, container_src = locate_container(extract_root)

    target = File.join(installed_dir, ext_id)
    if Dir.exist?(target) && !force
      raise Error, "extension #{ext_id.inspect} already installed at #{target} (pass force: true to overwrite)"
    end

    FileUtils.mkdir_p(installed_dir)
    FileUtils.rm_rf(target) if Dir.exist?(target)
    FileUtils.cp_r(container_src, target)

    Clacky::ExtensionLoader.invalidate_cache!
    units = verify_installed(ext_id, installed_dir)

    Result.new(ext_id: ext_id, path: target, units: units)
  end
end

.pack(ext_id, source_dir: Clacky::ExtensionLoader::LOCAL_DIR, out_dir: Dir.pwd) ⇒ Object

Zip up a single container from the local layer into out_dir. Runs verify first (blocking on errors) and refuses protected/encrypted units — those are produced by the platform's encryption pipeline, not hand-packed. Returns a Result with the produced zip path.

Raises:



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/clacky/extension/packager.rb', line 29

def pack(ext_id, source_dir: Clacky::ExtensionLoader::LOCAL_DIR, out_dir: Dir.pwd)
  slug = ext_id.to_s.strip
  raise Error, "invalid extension id: #{ext_id.inspect}" if slug.empty?

  container_dir = File.join(source_dir, slug)
  unless File.directory?(container_dir) && File.file?(File.join(container_dir, MANIFEST))
    raise Error, "no container found at #{container_dir} (expected #{MANIFEST})"
  end

  verify_container!(slug, container_dir)
  refuse_protected!(slug, container_dir)

  FileUtils.mkdir_p(out_dir)
  zip_path = File.join(out_dir, "#{slug}.zip")
  File.delete(zip_path) if File.exist?(zip_path)

  write_zip(container_dir, zip_path)

  Result.new(ext_id: slug, path: zip_path, units: nil)
end