Class: Rwm::TaskCache
- Inherits:
-
Object
- Object
- Rwm::TaskCache
- Defined in:
- lib/rwm/task_cache.rb
Class Method Summary collapse
Instance Method Summary collapse
-
#cache_declarations(package) ⇒ Object
Discover cacheable task declarations by running ‘bundle exec rake rwm:cache_config`.
-
#cacheable?(package, task) ⇒ Boolean
Returns true if the task is declared cacheable in the package’s Rakefile.
-
#cached?(package, task) ⇒ Boolean
Returns true if the (package, task) pair is cached and inputs haven’t changed.
-
#content_hash(package) ⇒ Object
Compute a content hash for a package: SHA256 of all source files + dependency hashes.
-
#initialize(workspace, graph) ⇒ TaskCache
constructor
A new instance of TaskCache.
-
#outputs_exist?(package, output_pattern) ⇒ Boolean
Check if declared output files/globs exist in the package directory.
-
#store(package, task) ⇒ Object
Store the current content hash after a successful task run.
Constructor Details
#initialize(workspace, graph) ⇒ TaskCache
Returns a new instance of TaskCache.
21 22 23 24 25 26 27 |
# File 'lib/rwm/task_cache.rb', line 21 def initialize(workspace, graph) @workspace = workspace @graph = graph @cache_dir = File.join(workspace.root, ".rwm", "cache") @content_hashes = {} @cache_declarations = {} end |
Class Method Details
.clean(workspace, package_name: nil) ⇒ Object
10 11 12 13 14 15 16 17 18 19 |
# File 'lib/rwm/task_cache.rb', line 10 def self.clean(workspace, package_name: nil) cache_dir = File.join(workspace.root, ".rwm", "cache") return unless Dir.exist?(cache_dir) if package_name Dir.glob(File.join(cache_dir, "#{package_name}-*")).each { |f| File.delete(f) } else Dir.glob(File.join(cache_dir, "*")).each { |f| File.delete(f) } end end |
Instance Method Details
#cache_declarations(package) ⇒ Object
Discover cacheable task declarations by running ‘bundle exec rake rwm:cache_config`
102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/rwm/task_cache.rb', line 102 def cache_declarations(package) return @cache_declarations[package.name] if @cache_declarations.key?(package.name) Rwm.debug("cache declarations: discovering for #{package.name}") output, _, status = Open3.capture3("bundle", "exec", "rake", "rwm:cache_config", chdir: package.path) @cache_declarations[package.name] = if status.success? && !output.strip.empty? JSON.parse(output.strip) else {} end rescue JSON::ParserError @cache_declarations[package.name] = {} end |
#cacheable?(package, task) ⇒ Boolean
Returns true if the task is declared cacheable in the package’s Rakefile
30 31 32 33 |
# File 'lib/rwm/task_cache.rb', line 30 def cacheable?(package, task) declarations = cache_declarations(package) declarations.key?(task) end |
#cached?(package, task) ⇒ Boolean
Returns true if the (package, task) pair is cached and inputs haven’t changed. Also verifies declared outputs exist (if any).
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/rwm/task_cache.rb', line 37 def cached?(package, task) stored = read_stored_hash(package, task) unless stored Rwm.debug("cache miss: #{package.name}:#{task} (no stored hash)") return false end current = content_hash(package) unless stored == current Rwm.debug("cache miss: #{package.name}:#{task} (hash changed)") return false end # If outputs are declared, they must exist decl = cache_declarations(package)[task] if decl && decl["output"] unless outputs_exist?(package, decl["output"]) Rwm.debug("cache miss: #{package.name}:#{task} (outputs missing)") return false end end Rwm.debug("cache hit: #{package.name}:#{task}") true end |
#content_hash(package) ⇒ Object
Compute a content hash for a package: SHA256 of all source files + dependency hashes
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/rwm/task_cache.rb', line 78 def content_hash(package) return @content_hashes[package.name] if @content_hashes.key?(package.name) digest = Digest::SHA256.new # Hash all source files in the package (sorted for determinism) source_files(package).each do |file| rel_path = file.delete_prefix("#{package.path}/") digest.update(rel_path) digest.update(File.read(file)) end # Include dependency content hashes (transitive invalidation). # If a dependency is missing, let it raise — a stale graph should # not silently produce incorrect cache hits. @graph.dependencies(package.name).sort.each do |dep_name| dep_pkg = @workspace.find_package(dep_name) digest.update(content_hash(dep_pkg)) end @content_hashes[package.name] = digest.hexdigest end |
#outputs_exist?(package, output_pattern) ⇒ Boolean
Check if declared output files/globs exist in the package directory
72 73 74 75 |
# File 'lib/rwm/task_cache.rb', line 72 def outputs_exist?(package, output_pattern) matches = Dir.glob(File.join(package.path, output_pattern)) !matches.empty? end |
#store(package, task) ⇒ Object
Store the current content hash after a successful task run
64 65 66 67 68 69 |
# File 'lib/rwm/task_cache.rb', line 64 def store(package, task) Rwm.debug("cache store: #{package.name}:#{task}") FileUtils.mkdir_p(@cache_dir) path = cache_file(package, task) File.write(path, content_hash(package)) end |