dotsync 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0da622d8ef82d600401b7464c4ad28be7872fa942c252d00273378bcbdab2788
4
- data.tar.gz: db97edbc75bb40ddd8353d97d2b3d255da9caa298dde368a58963e1f000bbc2d
3
+ metadata.gz: c060e4f84252048c1b038e350efcf900bc5b62b6bab5285dc9b1a2a9d0d0da0f
4
+ data.tar.gz: 5bef34fde923690c3ffaf68b3d5ed77d2195006d91d8b4f2c59853533c285339
5
5
  SHA512:
6
- metadata.gz: f1f7fd2a2bb4d39df4c8a4286fb4ce750b55f5e3d4b0fb1a1bfa6a7902f5e6106919bb4dd1981057c583931fd4bf6534c64dc8161175caf38159bcb9dd5f93d5
7
- data.tar.gz: a79078d32865500b76f5a27959bc426523a73ea31ec966aee7085b4786e024f9b7716dce1cb014323848228c64706618b91a9dfcd1a8ff5913817f165996c426
6
+ metadata.gz: 7049966bedd67e25b008ad9a7bae1697f54d189dc7f666bd42495a242af68b8c9494ead4c3e5310e500ce516db47270d5ae0f0382035b50df5d09543a631192f
7
+ data.tar.gz: 7abedab0ed8d49946e5b832ad38390968f17c6a7e32da3a3d774a47117767f1eec9dc3cbefd20a2f815eed5b091dcab73c4e9612b21d67fb1b7f85a0b8499ed1
data/CHANGELOG.md CHANGED
@@ -1,3 +1,34 @@
1
+ ## [0.2.3] - 2026-02-08
2
+
3
+ **New Features:**
4
+ - Add per-mapping post-sync hooks that run commands after files are transferred
5
+ - `post_sync` — runs after sync in both directions (`[[sync]]` mappings)
6
+ - `post_push` — runs only after push (`[[sync]]` and `[[push]]` mappings)
7
+ - `post_pull` — runs only after pull (`[[sync]]` and `[[pull]]` mappings)
8
+ - Template variables: `{files}` (shell-quoted changed dest paths), `{src}`, `{dest}`
9
+ - Hooks only execute when files actually changed and only with `--apply`
10
+ - Hook failures log errors but do not abort remaining hooks or mappings
11
+ - Preview mode shows what commands would run without executing
12
+ - Add `HookError` error class for hook-related errors
13
+ - Add hook icon (󰜎) to mappings legend with custom icon support
14
+
15
+ **New Files:**
16
+ - `lib/dotsync/utils/hook_runner.rb` — HookRunner utility with execute, preview, and template expansion
17
+ - `spec/dotsync/utils/hook_runner_spec.rb` — Comprehensive tests for HookRunner
18
+
19
+ **Documentation:**
20
+ - Add "Post-Sync Hooks" section to README with hook types, examples, template variables, and real-world use cases
21
+ - Add post-sync hooks to Key Features and Table of Contents
22
+
23
+ **Testing:**
24
+ - Add 35 new test examples covering hooks across all layers
25
+ - HookRunner: template expansion, multiple commands, failure handling, shell-escaped paths, preview mode
26
+ - Mapping: hooks attribute, has_hooks?, hook icon display
27
+ - SyncMappings: direction resolution, array concatenation, shorthand hooks, validation of invalid keys
28
+ - PushActionConfig/PullActionConfig: hook extraction, validation of unidirectional constraints
29
+ - PushAction/PullAction: hook execution with changes, skipped without changes, skipped in dry-run
30
+ - Total: 467 examples, 0 failures | Line: 96.45% | Branch: 82.86%
31
+
1
32
  ## [0.2.2] - 2025-02-07
2
33
 
3
34
  **New Features:**
@@ -17,7 +48,6 @@
17
48
  - Add integration tests for glob patterns in FileTransfer (including force mode)
18
49
  - Add integration tests for glob patterns in DirectoryDiffer
19
50
  - All 432 tests pass with 96.29% line coverage
20
-
21
51
  ## [0.2.1] - 2025-02-06
22
52
 
23
53
  **Performance Optimizations:**
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dotsync (0.2.2)
4
+ dotsync (0.2.3)
5
5
  fileutils (~> 1.7.3)
6
6
  find (~> 0.2.0)
7
7
  listen (~> 3.9.0)
data/README.md CHANGED
@@ -19,6 +19,7 @@ Dotsync is a powerful Ruby gem for managing and synchronizing your dotfiles acro
19
19
  - **Smart Filtering**: Use `force`, `only`, and `ignore` options to precisely control what gets synced
20
20
  - **Automatic Backups**: Pull operations create timestamped backups for easy recovery
21
21
  - **Live Watching**: Continuously monitor and sync changes in real-time with `watch` command
22
+ - **Post-Sync Hooks**: Run commands automatically after files change (e.g., codesigning, chmod, service reload)
22
23
  - **Customizable Output**: Control verbosity and customize icons to match your preferences
23
24
  - **Auto-Updates**: Get notified when new versions are available
24
25
 
@@ -33,6 +34,7 @@ Dotsync is a powerful Ruby gem for managing and synchronizing your dotfiles acro
33
34
  - [Bidirectional Sync Mappings (Recommended)](#bidirectional-sync-mappings-recommended)
34
35
  - [Alternative: Unidirectional Mappings](#alternative-unidirectional-mappings)
35
36
  - [Mapping Options (force, only, ignore)](#force-only-and-ignore-options-in-mappings)
37
+ - [Post-Sync Hooks](#post-sync-hooks)
36
38
  - [Safety Features](#safety-features)
37
39
  - [Customizing Icons](#customizing-icons)
38
40
  - [Automatic Update Checks](#automatic-update-checks)
@@ -499,6 +501,84 @@ This configuration:
499
501
 
500
502
  These options apply when the source is a directory and are relevant for both `push` and `pull` operations.
501
503
 
504
+ #### Post-Sync Hooks
505
+
506
+ Hooks let you run commands automatically after a mapping's files are transferred. Hooks only execute when files actually changed, and only when using `--apply`.
507
+
508
+ ##### Hook Types
509
+
510
+ | Hook | Description | Valid in |
511
+ |------|-------------|----------|
512
+ | `post_sync` | Runs after sync in both directions | `[[sync]]` mappings |
513
+ | `post_push` | Runs only after push | `[[sync]]` and `[[push]]` mappings |
514
+ | `post_pull` | Runs only after pull | `[[sync]]` and `[[pull]]` mappings |
515
+
516
+ For sync mappings, hooks are resolved by direction:
517
+ - **Push**: `post_sync` + `post_push` commands
518
+ - **Pull**: `post_sync` + `post_pull` commands
519
+
520
+ ##### Examples
521
+
522
+ **Single command (shorthand mapping):**
523
+ ```toml
524
+ [[sync.xdg_bin]]
525
+ force = true
526
+
527
+ [sync.xdg_bin.hooks]
528
+ post_sync = "codesign -s - {files}"
529
+ ```
530
+
531
+ **Multiple commands (explicit sync):**
532
+ ```toml
533
+ [[sync.mappings]]
534
+ local = "$XDG_CONFIG_HOME/scripts"
535
+ remote = "$XDG_CONFIG_HOME_MIRROR/scripts"
536
+
537
+ [sync.mappings.hooks]
538
+ post_sync = ["codesign -s - {files}", "chmod 700 {files}"]
539
+ post_pull = "launchctl kickstart -k gui/$(id -u)/com.example.service"
540
+ ```
541
+
542
+ **Unidirectional mapping:**
543
+ ```toml
544
+ [[pull.mappings]]
545
+ src = "$DOTFILES_DIR/scripts"
546
+ dest = "$HOME/Scripts"
547
+
548
+ [pull.mappings.hooks]
549
+ post_pull = ["codesign -s - {files}", "chmod 700 {files}"]
550
+ ```
551
+
552
+ ##### Template Variables
553
+
554
+ | Variable | Description |
555
+ |----------|-------------|
556
+ | `{files}` | Shell-quoted paths of changed destination files |
557
+ | `{src}` | The mapping's source path |
558
+ | `{dest}` | The mapping's destination path |
559
+
560
+ ##### Real-World Examples
561
+
562
+ ```toml
563
+ # Codesign scripts after pulling (macOS Ventura+ requirement for LaunchAgents)
564
+ [[sync.xdg_bin]]
565
+ force = true
566
+
567
+ [sync.xdg_bin.hooks]
568
+ post_pull = "codesign -s - {files}"
569
+
570
+ # Reload a LaunchAgent after pulling config changes
571
+ [[pull.mappings]]
572
+ src = "$DOTFILES_DIR/LaunchAgents/com.example.plist"
573
+ dest = "$HOME/Library/LaunchAgents/com.example.plist"
574
+
575
+ [pull.mappings.hooks]
576
+ post_pull = "launchctl kickstart -k gui/$(id -u)/com.example"
577
+ ```
578
+
579
+ > [!NOTE]
580
+ > In preview mode (without `--apply`), hooks are displayed as a preview showing what commands would run. Hook failures log errors but do not abort remaining hooks or mappings.
581
+
502
582
  ### Safety Features
503
583
 
504
584
  Dotsync includes several safety mechanisms to prevent accidental data loss:
@@ -23,8 +23,9 @@ module Dotsync
23
23
 
24
24
  MAPPINGS_LEGEND = [
25
25
  [Icons.force, "The source will overwrite the destination"],
26
- [Icons.only, "Paths designated explicitly as source only"],
27
- [Icons.ignore, "Paths configured to be ignored in the destination"],
26
+ [Icons.only, "Filtered by 'only' whitelist"],
27
+ [Icons.ignore, "Filtered by 'ignore' blacklist"],
28
+ [Icons.hook, "Post-sync hooks configured"],
28
29
  [Icons.invalid, "Invalid paths detected in the source or destination"]
29
30
  ]
30
31
 
@@ -135,6 +136,42 @@ module Dotsync
135
136
  end
136
137
  end
137
138
 
139
+ def execute_hooks
140
+ valid_mappings.each_with_index do |mapping, idx|
141
+ next unless mapping.has_hooks?
142
+
143
+ differ = differs[idx]
144
+ changed_files = differ.additions + differ.modifications
145
+ next if changed_files.empty?
146
+
147
+ runner = Dotsync::HookRunner.new(mapping: mapping, changed_files: changed_files, logger: logger)
148
+ runner.execute
149
+ end
150
+ end
151
+
152
+ def show_hooks_preview
153
+ hooks_to_run = []
154
+
155
+ valid_mappings.each_with_index do |mapping, idx|
156
+ next unless mapping.has_hooks?
157
+
158
+ differ = differs[idx]
159
+ changed_files = differ.additions + differ.modifications
160
+ next if changed_files.empty?
161
+
162
+ runner = Dotsync::HookRunner.new(mapping: mapping, changed_files: changed_files, logger: logger)
163
+ hooks_to_run.concat(runner.preview)
164
+ end
165
+
166
+ return if hooks_to_run.empty?
167
+
168
+ info("Hooks to run:", icon: :hook)
169
+ hooks_to_run.each do |command|
170
+ logger.log(" #{command}")
171
+ end
172
+ logger.log("")
173
+ end
174
+
138
175
  private
139
176
  # Computes diffs for all valid mappings.
140
177
  #
@@ -16,6 +16,7 @@ module Dotsync
16
16
  show_mappings if output_sections[:mappings]
17
17
  show_differences_legend if has_differences? && output_sections[:differences_legend]
18
18
  show_differences(diff_content: output_sections[:diff_content]) if output_sections[:differences]
19
+ show_hooks_preview if output_sections[:differences]
19
20
 
20
21
  return unless options[:apply]
21
22
 
@@ -32,6 +33,7 @@ module Dotsync
32
33
  end
33
34
 
34
35
  transfer_mappings
36
+ execute_hooks
35
37
  action("Mappings pulled", icon: :done)
36
38
  end
37
39
 
@@ -14,6 +14,7 @@ module Dotsync
14
14
  show_mappings if output_sections[:mappings]
15
15
  show_differences_legend if has_differences? && output_sections[:differences_legend]
16
16
  show_differences(diff_content: output_sections[:diff_content]) if output_sections[:differences]
17
+ show_hooks_preview if output_sections[:differences]
17
18
 
18
19
  return unless options[:apply]
19
20
 
@@ -23,6 +24,7 @@ module Dotsync
23
24
  end
24
25
 
25
26
  transfer_mappings
27
+ execute_hooks
26
28
  action("Mappings pushed", icon: :done)
27
29
  end
28
30
  end
@@ -96,6 +96,12 @@ module Dotsync
96
96
  base["ignore"] = mapping["ignore"] if mapping.key?("ignore")
97
97
  base["only"] = mapping["only"] if mapping.key?("only")
98
98
 
99
+ # Resolve hooks for direction
100
+ if mapping.key?("hooks")
101
+ resolved = resolve_hooks_for_direction(mapping["hooks"], direction)
102
+ base["hooks"] = resolved if resolved.any?
103
+ end
104
+
99
105
  base
100
106
  end
101
107
 
@@ -142,9 +148,41 @@ module Dotsync
142
148
  base["ignore"] = mapping["ignore"] if mapping.key?("ignore")
143
149
  base["only"] = only if only
144
150
 
151
+ # Resolve hooks for direction
152
+ if mapping.key?("hooks")
153
+ resolved = resolve_hooks_for_direction(mapping["hooks"], direction)
154
+ base["hooks"] = resolved if resolved.any?
155
+ end
156
+
145
157
  base
146
158
  end
147
159
 
160
+ def resolve_hooks_for_direction(raw_hooks, direction)
161
+ return [] unless raw_hooks.is_a?(Hash)
162
+
163
+ hooks = Array(raw_hooks["post_sync"])
164
+ case direction
165
+ when :push
166
+ hooks += Array(raw_hooks["post_push"])
167
+ when :pull
168
+ hooks += Array(raw_hooks["post_pull"])
169
+ end
170
+ hooks
171
+ end
172
+
173
+ def validate_hooks!(hooks, context)
174
+ return unless hooks.is_a?(Hash)
175
+
176
+ valid_keys = %w[post_sync post_push post_pull]
177
+ invalid_keys = hooks.keys - valid_keys
178
+ return if invalid_keys.empty?
179
+
180
+ raise Dotsync::ConfigError,
181
+ "Configuration error in #{context}: " \
182
+ "Invalid hook key(s): #{invalid_keys.join(", ")}. " \
183
+ "Valid keys are: #{valid_keys.join(", ")}"
184
+ end
185
+
148
186
  def build_path(base, path)
149
187
  path ? File.join(base, path) : base
150
188
  end
@@ -171,6 +209,8 @@ module Dotsync
171
209
  "Configuration error in sync.mappings ##{index + 1}: " \
172
210
  "Each mapping must have 'local' and 'remote' keys."
173
211
  end
212
+
213
+ validate_hooks!(mapping["hooks"], "sync.mappings ##{index + 1}") if mapping.key?("hooks")
174
214
  end
175
215
  end
176
216
 
@@ -184,6 +224,8 @@ module Dotsync
184
224
  "Configuration error in sync.#{shorthand_type} ##{index + 1}: " \
185
225
  "Each mapping must be a table."
186
226
  end
227
+
228
+ validate_hooks!(mapping["hooks"], "sync.#{shorthand_type} ##{index + 1}") if mapping.is_a?(Hash) && mapping.key?("hooks")
187
229
  end
188
230
  end
189
231
  end
@@ -22,7 +22,15 @@ module Dotsync
22
22
 
23
23
  def section_mappings
24
24
  return [] unless section && section["mappings"]
25
- Array(section["mappings"]).map { |mapping| Dotsync::Mapping.new(mapping) }
25
+ Array(section["mappings"]).map do |mapping|
26
+ attrs = mapping.dup
27
+ if attrs.key?("hooks") && attrs["hooks"].is_a?(Hash)
28
+ resolved = Array(attrs["hooks"]["post_pull"])
29
+ attrs["hooks"] = resolved.any? ? resolved : nil
30
+ attrs.delete("hooks") unless attrs["hooks"]
31
+ end
32
+ Dotsync::Mapping.new(attrs)
33
+ end
26
34
  end
27
35
 
28
36
  def validate!
@@ -46,6 +54,16 @@ module Dotsync
46
54
  unless mapping.is_a?(Hash) && mapping.key?("src") && mapping.key?("dest")
47
55
  raise "Configuration error in pull mapping ##{index + 1}: Each mapping must have 'src' and 'dest' keys."
48
56
  end
57
+
58
+ if mapping.is_a?(Hash) && mapping.key?("hooks") && mapping["hooks"].is_a?(Hash)
59
+ invalid_keys = mapping["hooks"].keys - ["post_pull"]
60
+ if invalid_keys.any?
61
+ raise Dotsync::ConfigError,
62
+ "Configuration error in pull mapping ##{index + 1}: " \
63
+ "Only 'post_pull' hooks are allowed in [pull] mappings. " \
64
+ "Invalid key(s): #{invalid_keys.join(", ")}"
65
+ end
66
+ end
49
67
  end
50
68
  end
51
69
  end
@@ -17,7 +17,15 @@ module Dotsync
17
17
 
18
18
  def section_mappings
19
19
  return [] unless section && section["mappings"]
20
- Array(section["mappings"]).map { |mapping| Dotsync::Mapping.new(mapping) }
20
+ Array(section["mappings"]).map do |mapping|
21
+ attrs = mapping.dup
22
+ if attrs.key?("hooks") && attrs["hooks"].is_a?(Hash)
23
+ resolved = Array(attrs["hooks"]["post_push"])
24
+ attrs["hooks"] = resolved.any? ? resolved : nil
25
+ attrs.delete("hooks") unless attrs["hooks"]
26
+ end
27
+ Dotsync::Mapping.new(attrs)
28
+ end
21
29
  end
22
30
 
23
31
  def validate!
@@ -41,6 +49,16 @@ module Dotsync
41
49
  unless mapping.is_a?(Hash) && mapping.key?("src") && mapping.key?("dest")
42
50
  raise "Configuration error in push mapping ##{index + 1}: Each mapping must have 'src' and 'dest' keys."
43
51
  end
52
+
53
+ if mapping.is_a?(Hash) && mapping.key?("hooks") && mapping["hooks"].is_a?(Hash)
54
+ invalid_keys = mapping["hooks"].keys - ["post_push"]
55
+ if invalid_keys.any?
56
+ raise Dotsync::ConfigError,
57
+ "Configuration error in push mapping ##{index + 1}: " \
58
+ "Only 'post_push' hooks are allowed in [push] mappings. " \
59
+ "Invalid key(s): #{invalid_keys.join(", ")}"
60
+ end
61
+ end
44
62
  end
45
63
  end
46
64
  end
@@ -8,4 +8,5 @@ module Dotsync
8
8
  class DiskFullError < FileTransferError; end
9
9
  class SymlinkError < FileTransferError; end
10
10
  class TypeConflictError < FileTransferError; end
11
+ class HookError < Error; end
11
12
  end
data/lib/dotsync/icons.rb CHANGED
@@ -18,6 +18,7 @@ module Dotsync
18
18
  DEFAULT_ONLY = " "
19
19
  DEFAULT_IGNORE = "󰈉 "
20
20
  DEFAULT_INVALID = "󱏏 "
21
+ DEFAULT_HOOK = "󰜎 "
21
22
 
22
23
  # Default Mappings Differences icons
23
24
  DEFAULT_DIFF_CREATED = " "
@@ -50,6 +51,7 @@ module Dotsync
50
51
  only: config.dig("icons", "only") || DEFAULT_ONLY,
51
52
  ignore: config.dig("icons", "ignore") || DEFAULT_IGNORE,
52
53
  invalid: config.dig("icons", "invalid") || DEFAULT_INVALID,
54
+ hook: config.dig("icons", "hook") || DEFAULT_HOOK,
53
55
  # Differences Legend
54
56
  diff_created: config.dig("icons", "diff_created") || DEFAULT_DIFF_CREATED,
55
57
  diff_updated: config.dig("icons", "diff_updated") || DEFAULT_DIFF_UPDATED,
@@ -75,6 +77,10 @@ module Dotsync
75
77
  @custom_icons[:invalid] || DEFAULT_INVALID
76
78
  end
77
79
 
80
+ def self.hook
81
+ @custom_icons[:hook] || DEFAULT_HOOK
82
+ end
83
+
78
84
  # Differences Legend methods
79
85
 
80
86
  def self.diff_created
@@ -100,6 +106,7 @@ module Dotsync
100
106
  diff: DIFF,
101
107
  force: -> { force },
102
108
  ignore: -> { ignore },
109
+ hook: DEFAULT_HOOK,
103
110
  pull: PULL,
104
111
  push: PUSH,
105
112
  watch: WATCH,
@@ -13,6 +13,7 @@ require_relative "../utils/directory_differ"
13
13
  require_relative "../utils/config_cache"
14
14
  require_relative "../utils/content_diff"
15
15
  require_relative "../utils/parallel"
16
+ require_relative "../utils/hook_runner"
16
17
 
17
18
  # Models
18
19
  require_relative "../models/mapping"
@@ -13,6 +13,7 @@ require_relative "../utils/directory_differ"
13
13
  require_relative "../utils/config_cache"
14
14
  require_relative "../utils/content_diff"
15
15
  require_relative "../utils/parallel"
16
+ require_relative "../utils/hook_runner"
16
17
 
17
18
  # Models
18
19
  require_relative "../models/mapping"
@@ -29,6 +29,7 @@ module Dotsync
29
29
  @original_ignores = Array(attributes["ignore"])
30
30
  @original_only = Array(attributes["only"])
31
31
  @force = attributes["force"] || false
32
+ @hooks = Array(attributes["hooks"])
32
33
 
33
34
  @sanitized_src, @sanitized_dest, @sanitized_ignores, @sanitized_only = process_paths(
34
35
  @original_src,
@@ -58,6 +59,12 @@ module Dotsync
58
59
  @force
59
60
  end
60
61
 
62
+ attr_reader :hooks
63
+
64
+ def has_hooks?
65
+ @hooks.any?
66
+ end
67
+
61
68
  def directories?
62
69
  File.directory?(src) && File.directory?(dest)
63
70
  end
@@ -103,6 +110,7 @@ module Dotsync
103
110
  msg << Icons.force if force?
104
111
  msg << Icons.only if has_inclusions?
105
112
  msg << Icons.ignore if has_ignores?
113
+ msg << Icons.hook if has_hooks?
106
114
  msg << Icons.invalid unless valid?
107
115
  msg.join
108
116
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+ require "shellwords"
5
+
6
+ module Dotsync
7
+ class HookRunner
8
+ def initialize(mapping:, changed_files:, logger:)
9
+ @mapping = mapping
10
+ @changed_files = changed_files
11
+ @logger = logger
12
+ end
13
+
14
+ def execute
15
+ @mapping.hooks.map do |command|
16
+ expanded = expand_template(command)
17
+ run_command(expanded)
18
+ end
19
+ end
20
+
21
+ def preview
22
+ @mapping.hooks.map { |command| expand_template(command) }
23
+ end
24
+
25
+ private
26
+ def expand_template(command)
27
+ files_str = @changed_files.map { |f| Shellwords.escape(f) }.join(" ")
28
+
29
+ command
30
+ .gsub("{files}", files_str)
31
+ .gsub("{src}", @mapping.src)
32
+ .gsub("{dest}", @mapping.dest)
33
+ end
34
+
35
+ def run_command(command)
36
+ stdout, stderr, status = Open3.capture3(command)
37
+
38
+ if status.success?
39
+ @logger.info("Hook succeeded: #{command}", icon: :hook)
40
+ else
41
+ @logger.error("Hook failed: #{command}")
42
+ @logger.error(" #{stderr.strip}") unless stderr.strip.empty?
43
+ end
44
+
45
+ { command: command, stdout: stdout, stderr: stderr, success: status.success? }
46
+ end
47
+ end
48
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dotsync
4
- VERSION = "0.2.2"
4
+ VERSION = "0.2.3"
5
5
  end
data/lib/dotsync.rb CHANGED
@@ -32,6 +32,7 @@ require_relative "dotsync/utils/directory_differ"
32
32
  require_relative "dotsync/utils/version_checker"
33
33
  require_relative "dotsync/utils/config_cache"
34
34
  require_relative "dotsync/utils/parallel"
35
+ require_relative "dotsync/utils/hook_runner"
35
36
 
36
37
  # Models
37
38
  require_relative "dotsync/models/mapping"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dotsync
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Sáenz
@@ -337,6 +337,7 @@ files:
337
337
  - lib/dotsync/utils/content_diff.rb
338
338
  - lib/dotsync/utils/directory_differ.rb
339
339
  - lib/dotsync/utils/file_transfer.rb
340
+ - lib/dotsync/utils/hook_runner.rb
340
341
  - lib/dotsync/utils/logger.rb
341
342
  - lib/dotsync/utils/parallel.rb
342
343
  - lib/dotsync/utils/path_utils.rb