Module: DocOpsLab::Dev::Library
- Defined in:
- lib/docopslab/dev/library.rb,
lib/docopslab/dev/library/cache.rb,
lib/docopslab/dev/library/fetch.rb
Overview
Remote library fetch, cache, and resolution. Manages a host-wide asset cache at ~/.cache/docopslab/dev/library/. Callers should use this module directly: Library.fetch!, Library.resolve(path), etc.
Defined Under Namespace
Class Method Summary collapse
-
.available? ⇒ Boolean
True if a library is available via cache or local_path fallback.
- .cached_path ⇒ Object
-
.ensure_available! ⇒ Object
Ensure the library is available, auto-fetching if necessary.
- .fetch!(config = nil) ⇒ Object
-
.print_catalog_comparison(manifest = nil) ⇒ Object
Compare manifest catalog entries against the cached library files Falls back to an on-repo local path if provided in the manifest.
- .print_status ⇒ Object
-
.resolve(relative_path) ⇒ Object
Returns the absolute path to a cached file, or nil if absent.
- .rollback! ⇒ Object
-
.root ⇒ Object
Returns the effective library root directory (nil if unavailable).
-
.stage!(source_path: nil) ⇒ Object
Copy a local library directory into the host cache and sync content to manifest-configured paths.
- .status ⇒ Object
-
.sync!(force: false) ⇒ Object
Fetch the library if the cache is absent or stale, then sync all manifest-driven content (docs, config files, templates, scripts) to local paths.
Class Method Details
.available? ⇒ Boolean
True if a library is available via cache or local_path fallback.
131 132 133 134 135 136 |
# File 'lib/docopslab/dev/library.rb', line 131 def available? return true if Cache.available? lp = Dev.load_manifest&.dig('library', 'local_path') !!(lp && File.exist?(File.join(lp, 'catalog.json'))) end |
.cached_path ⇒ Object
93 94 95 |
# File 'lib/docopslab/dev/library.rb', line 93 def cached_path Cache.current_path end |
.ensure_available! ⇒ Object
Ensure the library is available, auto-fetching if necessary. Returns true if available after the call; raises on failure.
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/docopslab/dev/library.rb', line 140 def ensure_available! return true if available? puts '📥 Library cache not found; fetching now...' ok = fetch! return true if ok && available? lp = Dev.load_manifest&.dig('library', 'local_path') if lp && Dir.exist?(lp) warn "⚠️ Remote fetch failed; using local_path fallback: #{lp}" return true end raise 'Library unavailable. Run `bundle exec rake labdev:sync:library` to fetch it.' end |
.fetch!(config = nil) ⇒ Object
18 19 20 21 |
# File 'lib/docopslab/dev/library.rb', line 18 def fetch! config=nil config ||= library_config_from_manifest with_cache_root(config) { Fetch.call(config) } end |
.print_catalog_comparison(manifest = nil) ⇒ Object
Compare manifest catalog entries against the cached library files Falls back to an on-repo local path if provided in the manifest
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
# File 'lib/docopslab/dev/library.rb', line 191 def print_catalog_comparison manifest = nil manifest ||= Dev.load_manifest lib_cfg = manifest && manifest['library'] if lib_cfg.nil? || lib_cfg.empty? puts "ℹ️ No `library` block found in #{Dev.manifest_path} (or it's empty)." return end catalog = lib_cfg.dig('catalog', 'overrides') || lib_cfg['catalog'] || lib_cfg['catalog_overrides'] unless catalog && !catalog.empty? puts 'ℹ️ No catalog overrides found in manifest.library.catalog; nothing to compare.' return end puts '🔎 Comparing manifest catalog entries to cached library files...' entries = [] case catalog when Array entries = catalog when Hash catalog.each do |k, v| entries << if v.is_a?(String) v elsif v.is_a?(Hash) && v['path'] v['path'] else k end end else puts "⚠️ Unrecognized catalog format: #{catalog.class}. Skipping detailed compare." return end missing = [] present = [] entries.each do |rel_path| rel = rel_path.to_s.sub(%r{^/}, '') resolved = resolve(rel) # Fallback to on-repo local path if provided if resolved.nil? && lib_cfg['local_path'] repo_local = File.join(Dir.pwd, lib_cfg['local_path'].to_s, rel) resolved = File.exist?(repo_local) ? repo_local : nil end if resolved present << { path: rel, full: resolved } else missing << rel end end if present.any? puts "✅ Found #{present.size} catalog entries in the cache or local path:" present.each do |p| puts " - #{p[:path]} -> #{p[:full]}" end end if missing.any? puts "❌ Missing #{missing.size} catalog entries in the cache/local path:" missing.each do |m| puts " - #{m}" end else puts '✅ All catalog entries present in cache/local path.' end end |
.print_status ⇒ Object
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/docopslab/dev/library.rb', line 170 def print_status s = status if s[:available] puts "📚 Library cache: #{s[:cache_path]}" puts " Version : #{s[:version] || '(unknown)'}" puts " Ref : #{s[:ref] || '(unknown)'}" puts " Generated : #{s[:generated_at] || '(unknown)'}" puts " Previous : #{s[:has_previous] ? 'yes' : 'none'}" else puts "⚠️ No library cache found at #{s[:cache_path]}" lp = Dev.load_manifest&.dig('library', 'local_path') if lp && File.exist?(File.join(lp, 'catalog.json')) puts " Local path : #{File.(lp)} (active fallback)" else puts ' Run `bundle exec rake labdev:sync:library` to fetch.' end end end |
.resolve(relative_path) ⇒ Object
Returns the absolute path to a cached file, or nil if absent. Resolution order:
1. XDG host cache (~/.cache/docopslab/dev/library/current/)
2. local_path from manifest (dev/monorepo fallback, e.g. .library/)
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/docopslab/dev/library.rb', line 114 def resolve relative_path if Cache.available? full_path = File.join(Cache.current_path, relative_path) return full_path if File.exist?(full_path) end # local_path fallback for monorepo dev and offline use lp = Dev.load_manifest&.dig('library', 'local_path') if lp local_full = File.(File.join(lp, relative_path)) return local_full if File.exist?(local_full) end nil end |
.rollback! ⇒ Object
160 161 162 163 164 165 166 167 168 |
# File 'lib/docopslab/dev/library.rb', line 160 def rollback! if Cache.rollback! puts "✅ Library rolled back to previous snapshot at #{Cache.current_path}" true else warn '⚠️ No previous library snapshot available for rollback.' false end end |
.root ⇒ Object
Returns the effective library root directory (nil if unavailable). Does not auto-fetch; call ensure_available! first if needed. Resolution order mirrors resolve():
1. XDG host cache 2. local_path from manifest
101 102 103 104 105 106 107 108 |
# File 'lib/docopslab/dev/library.rb', line 101 def root return Cache.current_path if Cache.available? lp = Dev.load_manifest&.dig('library', 'local_path') return File.(lp) if lp && File.exist?(File.join(lp, 'catalog.json')) nil end |
.stage!(source_path: nil) ⇒ Object
Copy a local library directory into the host cache and sync content to manifest-configured paths. Intended for development workflows where assets live in the lab monorepo (.library/ or library/current/) and have not yet been published to the remote branch.
Resolution order for source_path:
1. Explicit argument (task arg or direct call)
2. manifest +library.local_path+ (resolved relative to cwd)
3. .library/ in the current working directory
4. ../lab/.library/ relative to cwd (downstream-project fallback)
A minimal catalog.json is generated into a staging copy if the source directory does not already contain one.
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/docopslab/dev/library.rb', line 63 def stage! source_path: nil resolved = resolve_stage_source(source_path) unless resolved warn '⚠️ No local library path found. ' \ "Pass a path, or set library.local_path in #{Dev::MANIFEST_PATH}." return false end puts "📦 Staging local library from #{resolved}..." Dir.mktmpdir('docopslab-stage-') do |tmpdir| dest = File.join(tmpdir, 'stage') FileUtils.cp_r(resolved, dest) ensure_catalog!(dest) Cache.write!(dest) end puts "✅ Local library staged to #{Cache.current_path}" context = Dev SyncOps.sync_config_files(context) SyncOps.sync_docs(context, force: true) SyncOps.sync_templates(context, force: true) SyncOps.sync_scripts(context) true rescue StandardError => e warn "⚠️ Stage failed: #{e.}" false end |
.status ⇒ Object
156 157 158 |
# File 'lib/docopslab/dev/library.rb', line 156 def status Cache.status end |
.sync!(force: false) ⇒ Object
Fetch the library if the cache is absent or stale, then sync all manifest-driven content (docs, config files, templates, scripts) to local paths. This is the main entry point for ‘labdev:sync:library`.
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/docopslab/dev/library.rb', line 26 def sync! force: false config = library_config_from_manifest with_cache_root(config) do if local_path_active?(config) puts "📚 Using local library at #{File.(config['local_path'])}" elsif !force && Cache.available? && sha_current?(config) puts "\u2705 Library cache is up to date (#{Cache.stored_head&.slice(0, 8)})" else puts Cache.available? ? '🔄 Library has updates; refreshing...' : '📥 Library cache not found; fetching...' ok = Fetch.call(config) unless ok warn '⚠️ Library fetch failed. Using existing cache if available.' raise 'Library unavailable.' unless available? end end context = Dev SyncOps.sync_config_files(context) SyncOps.sync_docs(context, force: force) SyncOps.sync_templates(context, force: force) SyncOps.sync_scripts(context) end end |