From 6e00a1c00a17da5df835651f22799675075b0eff Mon Sep 17 00:00:00 2001 From: Roberto Alsina Date: Wed, 12 Jun 2024 15:24:00 -0300 Subject: [PATCH] Auto mode for watched directories --- CHANGES.md | 4 ++++ spec/croupier_spec.cr | 25 +++++++++++++++++++++++++ spec/testcases/a_dir/tasks.yml | 12 ++++++++++++ src/croupier.cr | 24 ++++++++++++++++-------- 4 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 spec/testcases/a_dir/tasks.yml diff --git a/CHANGES.md b/CHANGES.md index 98c0414..3aeadcc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Changelog +## New in main + +* Auto mode works for watched directories + ## New in 0.5.1 * Add support for tasks depending on directories diff --git a/spec/croupier_spec.cr b/spec/croupier_spec.cr index 65df3d3..cca658b 100644 --- a/spec/croupier_spec.cr +++ b/spec/croupier_spec.cr @@ -1122,6 +1122,31 @@ describe "TaskManager" do x.should eq 2 end end + + it "should run tasks if a watched folder is created" do + with_scenario("a_dir") do + File.exists?("output3").should be_false + TaskManager.auto_run + sleep 0.02.seconds + Dir.mkdir("a_dir") + sleep 0.02.seconds + File.exists?("output3").should be_true + TaskManager.auto_stop + end + end + + it "should run tasks if a file is created inside a watched folder" do + with_scenario("a_dir") do + Dir.mkdir("a_dir") + TaskManager.auto_run + sleep 0.02.seconds + File.exists?("output3").should be_false + File.open("a_dir/input", "w") << "bar" + sleep 0.02.seconds + File.exists?("output3").should be_true + TaskManager.auto_stop + end + end end describe "save_run" do diff --git a/spec/testcases/a_dir/tasks.yml b/spec/testcases/a_dir/tasks.yml new file mode 100644 index 0000000..d916f9d --- /dev/null +++ b/spec/testcases/a_dir/tasks.yml @@ -0,0 +1,12 @@ +--- +output3: + id: 0a1aac149aec98f492c33b9d16ed614f59a68ad2 + name: name + inputs: + - a_dir + outputs: + - output3 + always_run: false + no_save: false + stale: true + procs: "counter" diff --git a/src/croupier.cr b/src/croupier.cr index eb6ef37..3111910 100644 --- a/src/croupier.cr +++ b/src/croupier.cr @@ -108,7 +108,7 @@ module Croupier # Refuse to merge if this task or any of the colliding ones # are not mergeable raise "Can't merge task #{self} with #{to_merge[..-2].map(&.to_s)}" \ - if to_merge.size > 1 && to_merge.any? { |t| !t.mergeable? } + if to_merge.size > 1 && to_merge.any? { |t| !t.mergeable? } reduced = to_merge.reduce { |t1, t2| t1.merge t2 } reduced.keys.each { |k| TaskManager.tasks[k] = reduced } end @@ -505,7 +505,7 @@ module Croupier if File.directory? path digest = Digest::SHA1.digest do |ctx| # Hash the directory tree - ctx.update(Dir.glob("#{path}/**/*").join("\n")) + ctx.update(Dir.glob("#{path}/**/*").sort().join("\n")) if !@fast_dirs # Hash *everything* in the directory (this will be slow) Dir.glob("#{path}/**/*").each do |f| @@ -536,7 +536,7 @@ module Croupier !File.exists?(input) } raise "Can't run: Unknown inputs #{bad_inputs.join(", ")}" \ - unless bad_inputs.empty? + unless bad_inputs.empty? end # Run all stale tasks in dependency order @@ -737,16 +737,24 @@ module Croupier @@watcher.on_event do |event| # It's a file we care about, add it to the queue - path = event.name || event.path + path = Path["#{event.path}/#{event.name}"].normalize.to_s Log.debug { "Detected change in #{path}" } Log.trace { "Event: #{event}" } - @queued_changes << path.to_s if target_inputs.includes? path.to_s + # If path matches a watched path, add it to the queue + @queued_changes << path if target_inputs.includes? path + # If we are watching a folder in path, add the folder to the queue + is_prefix = target_inputs.any? { |input| path.starts_with? "#{input}/" } + @queued_changes << path if is_prefix end - watch_flags = LibInotify::IN_DELETE | - LibInotify::IN_CREATE | - LibInotify::IN_MODIFY | + watch_flags = LibInotify::IN_DELETE | + LibInotify::IN_CREATE | + LibInotify::IN_MODIFY | + LibInotify::IN_MOVED_TO | LibInotify::IN_CLOSE_WRITE + # NOT watching IN_DELETE_SELF, IN_MOVE_SELF because + # when those are triggered we have no input file to + # process. target_inputs.each do |input| # Don't watch for changes in k/v store