Module: Textus::Ports::Publisher
- Defined in:
- lib/textus/ports/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.
Sentinel I/O is delegated to Textus::Ports::SentinelStore. 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.
Class Method Summary collapse
- .adoptable?(source, target) ⇒ Boolean
-
.guard_clobber(source, target, store_root) ⇒ Object
Refuse to clobber an unmanaged target — EXCEPT adopt one whose bytes already equal the source (ADR 0050: a migration copies files into the store and publishes them back to where they already live, so the target is byte-identical — nothing is at risk).
- .managed?(target, store_root) ⇒ Boolean
- .publish(source:, target:, store_root:) ⇒ Object
-
.unpublish(target:, store_root:) ⇒ Object
Removes a previously-published file and its sentinel.
Class Method Details
.adoptable?(source, target) ⇒ Boolean
46 47 48 |
# File 'lib/textus/ports/publisher.rb', line 46 def self.adoptable?(source, target) !File.symlink?(target) && File.file?(target) && FileUtils.identical?(source, target) end |
.guard_clobber(source, target, store_root) ⇒ Object
Refuse to clobber an unmanaged target — EXCEPT adopt one whose bytes already equal the source (ADR 0050: a migration copies files into the store and publishes them back to where they already live, so the target is byte-identical — nothing is at risk). Adoption writes no sentinel here; the normal publish path below does, and the cp is a content no-op. An unmanaged target whose content DIFFERS, or any unmanaged symlink, is still refused — that is the guard’s real job.
38 39 40 41 42 43 44 |
# File 'lib/textus/ports/publisher.rb', line 38 def self.guard_clobber(source, target, store_root) return unless File.exist?(target) || File.symlink?(target) return if managed?(target, store_root) return if adoptable?(source, target) raise PublishError.new("refusing to clobber unmanaged file at #{target}", target: target) end |
.managed?(target, store_root) ⇒ Boolean
50 51 52 |
# File 'lib/textus/ports/publisher.rb', line 50 def self.managed?(target, store_root) File.exist?(Textus::Ports::SentinelStore.new.sentinel_path(target, store_root)) end |
.publish(source:, target:, store_root:) ⇒ Object
13 14 15 16 17 18 19 |
# File 'lib/textus/ports/publisher.rb', line 13 def self.publish(source:, target:, store_root:) FileUtils.mkdir_p(File.dirname(target)) guard_clobber(source, target, store_root) File.delete(target) if File.symlink?(target) FileUtils.cp(source, target) Textus::Ports::SentinelStore.new.write!(target: target, source: source, store_root: store_root) end |
.unpublish(target:, store_root:) ⇒ Object
Removes a previously-published file and its sentinel. No-op unless the target is textus-managed — never deletes an unmanaged file.
23 24 25 26 27 28 29 |
# File 'lib/textus/ports/publisher.rb', line 23 def self.unpublish(target:, store_root:) return unless managed?(target, store_root) FileUtils.rm_f(target) sentinel = Textus::Ports::SentinelStore.new.sentinel_path(target, store_root) FileUtils.rm_f(sentinel) end |