Module: Textus::Publisher
- Defined in:
- lib/textus/publisher.rb
Overview
Publishes built artifacts from the store to repo-relative consumer paths. Publish = copy + sentinel. The in-store file is already the consumer-shaped artifact; no parsing or stripping. Sentinels live under ‘<store_root>/sentinels/` and mirror the target’s repo-relative layout so consumer directories aren’t polluted with ‘.textus-managed.json` siblings.
Constant Summary collapse
- SENTINEL_SUFFIX =
".textus-managed.json".freeze
- SENTINEL_DIR =
"sentinels".freeze
Class Method Summary collapse
- .cleanup_legacy_sentinel(target) ⇒ Object
- .legacy_sentinel_path(target) ⇒ Object
- .managed?(target, store_root) ⇒ Boolean
- .publish(source:, target:, store_root:) ⇒ Object
- .refuse_if_unmanaged(target, store_root) ⇒ Object
- .relative_to(path, base) ⇒ Object
-
.sentinel_path(target, store_root) ⇒ Object
Sentinel layout: <store_root>/sentinels/<target_rel_to_repo>.textus-managed.json The full target extension is preserved so a marketplace.json and marketplace.yaml don’t collide.
- .write_sentinel(target, store_root:, source:) ⇒ Object
Class Method Details
.cleanup_legacy_sentinel(target) ⇒ Object
59 60 61 |
# File 'lib/textus/publisher.rb', line 59 def self.cleanup_legacy_sentinel(target) FileUtils.rm_f(legacy_sentinel_path(target)) end |
.legacy_sentinel_path(target) ⇒ Object
55 56 57 |
# File 'lib/textus/publisher.rb', line 55 def self.legacy_sentinel_path(target) target + SENTINEL_SUFFIX end |
.managed?(target, store_root) ⇒ Boolean
31 32 33 |
# File 'lib/textus/publisher.rb', line 31 def self.managed?(target, store_root) File.exist?(sentinel_path(target, store_root)) || File.exist?(legacy_sentinel_path(target)) end |
.publish(source:, target:, store_root:) ⇒ Object
15 16 17 18 19 20 21 22 |
# File 'lib/textus/publisher.rb', line 15 def self.publish(source:, target:, store_root:) FileUtils.mkdir_p(File.dirname(target)) refuse_if_unmanaged(target, store_root) File.delete(target) if File.symlink?(target) FileUtils.cp(source, target) write_sentinel(target, store_root: store_root, source: source) cleanup_legacy_sentinel(target) end |
.refuse_if_unmanaged(target, store_root) ⇒ Object
24 25 26 27 28 29 |
# File 'lib/textus/publisher.rb', line 24 def self.refuse_if_unmanaged(target, store_root) return unless File.exist?(target) || File.symlink?(target) return if managed?(target, store_root) raise PublishError.new("refusing to clobber unmanaged file at #{target}", target: target) end |
.relative_to(path, base) ⇒ Object
63 64 65 66 67 68 69 |
# File 'lib/textus/publisher.rb', line 63 def self.relative_to(path, base) path = File.(path) base = File.(base) return nil unless path.start_with?(base + File::SEPARATOR) path[(base.length + 1)..] end |
.sentinel_path(target, store_root) ⇒ Object
Sentinel layout: <store_root>/sentinels/<target_rel_to_repo>.textus-managed.json The full target extension is preserved so a marketplace.json and marketplace.yaml don’t collide.
49 50 51 52 53 |
# File 'lib/textus/publisher.rb', line 49 def self.sentinel_path(target, store_root) repo_root = File.dirname(store_root) rel = relative_to(target, repo_root) || File.basename(target) File.join(store_root, SENTINEL_DIR, rel + SENTINEL_SUFFIX) end |
.write_sentinel(target, store_root:, source:) ⇒ Object
35 36 37 38 39 40 41 42 43 44 |
# File 'lib/textus/publisher.rb', line 35 def self.write_sentinel(target, store_root:, source:) path = sentinel_path(target, store_root) FileUtils.mkdir_p(File.dirname(path)) File.write(path, JSON.generate( "source" => source, "target" => target, "sha256" => Digest::SHA256.hexdigest(File.binread(target)), "mode" => "copy", )) end |