dotsync 0.1.23 → 0.1.25
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 +4 -4
- data/CHANGELOG.md +48 -0
- data/Gemfile.lock +1 -1
- data/README.md +100 -9
- data/exe/dotsync +51 -4
- data/lib/dotsync/config/base_config.rb +11 -1
- data/lib/dotsync/core.rb +35 -0
- data/lib/dotsync/loaders/pull_loader.rb +29 -0
- data/lib/dotsync/loaders/push_loader.rb +29 -0
- data/lib/dotsync/loaders/setup_loader.rb +20 -0
- data/lib/dotsync/loaders/watch_loader.rb +26 -0
- data/lib/dotsync/utils/config_cache.rb +86 -0
- data/lib/dotsync/utils/directory_differ.rb +1 -1
- data/lib/dotsync/version.rb +1 -1
- data/lib/dotsync.rb +4 -0
- metadata +8 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 793ae0a7e00707a97255d631304eddae60e6ce6224d8c4d85c9550f54cbd59bd
|
|
4
|
+
data.tar.gz: 34fdbefc7f9b27a2f7fedd8dcbac9748ae59c99fb7cb15a970ebc67605e5ebe1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 50471dd7fca75246adb01dd946b17dc217b590cc0df359253f608f4868ae190c08aeb5ce5c9302dafa2533f368c69f35b1f9b3f70143ed36066745729904030b
|
|
7
|
+
data.tar.gz: 2061b3db4dfb07aaec4750efba675091797ce8ebcb9979ce37ffca5e1b838f20d2ebec181925c45d5a0a6c52d4681d05f989953f4022540c0a6705dc8fc6dd76
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,51 @@
|
|
|
1
|
+
# 0.1.25
|
|
2
|
+
|
|
3
|
+
**Features:**
|
|
4
|
+
- Add support for file-specific paths in 'only' configuration option
|
|
5
|
+
- Enable specifying individual files within directories: `only = ["bundle/config", "ghc/ghci.conf"]`
|
|
6
|
+
- Parent directories are automatically created as needed
|
|
7
|
+
- Sibling files in the same directory remain unaffected
|
|
8
|
+
- Works with deeply nested paths: `only = ["nvim/lua/plugins/init.lua"]`
|
|
9
|
+
- Fix DirectoryDiffer to use bidirectional_include? for proper file traversal
|
|
10
|
+
|
|
11
|
+
**Documentation:**
|
|
12
|
+
- Completely rewrite 'force', 'only', and 'ignore' options section in README
|
|
13
|
+
- Add 4 detailed examples showing different use cases
|
|
14
|
+
- Add warnings and notes about combining options
|
|
15
|
+
- Document important behaviors and edge cases
|
|
16
|
+
|
|
17
|
+
**Testing:**
|
|
18
|
+
- Add comprehensive test coverage for file-specific paths in 'only' option
|
|
19
|
+
- Add tests for FileTransfer with nested file paths
|
|
20
|
+
- Add tests for DirectoryDiffer with file-specific only paths
|
|
21
|
+
- Add tests with force mode enabled
|
|
22
|
+
- All 396 tests pass with 96.61% line coverage
|
|
23
|
+
|
|
24
|
+
# 0.1.24
|
|
25
|
+
|
|
26
|
+
**Performance Optimizations:**
|
|
27
|
+
- Implement lazy loading to defer library loading until after argument parsing
|
|
28
|
+
- Reduces `--version` and `--help` startup time from ~900ms to ~380ms (2.4x faster)
|
|
29
|
+
- Full library only loaded when executing actual commands
|
|
30
|
+
- Add action-specific loaders to reduce memory footprint
|
|
31
|
+
- Each command loads only its required dependencies
|
|
32
|
+
- Push/pull skip loading 'listen' gem (only needed for watch)
|
|
33
|
+
- Watch skips terminal-table and diff logic
|
|
34
|
+
- Setup uses minimal dependencies for near-instant execution
|
|
35
|
+
- Add config caching with XDG_DATA_HOME integration
|
|
36
|
+
- Caches parsed TOML as Marshal binary (~180x faster to load)
|
|
37
|
+
- Automatic cache invalidation based on mtime, size, and version
|
|
38
|
+
- Graceful fallback to TOML parsing on errors
|
|
39
|
+
- Disable with `DOTSYNC_NO_CACHE=1` environment variable
|
|
40
|
+
- Saves ~14ms per invocation on typical configs
|
|
41
|
+
|
|
42
|
+
**Combined Performance Impact:**
|
|
43
|
+
- `--version`: 900ms → 380ms (2.4x faster)
|
|
44
|
+
- `--help`: 900ms → 380ms (2.4x faster)
|
|
45
|
+
- Setup: Near-instant with minimal loading
|
|
46
|
+
- Push/pull: ~15% faster with cached config
|
|
47
|
+
- Watch: ~40% faster without unnecessary dependencies
|
|
48
|
+
|
|
1
49
|
# 0.1.23
|
|
2
50
|
|
|
3
51
|
**Critical Bug Fix:**
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -264,16 +264,107 @@ dest = "$DOTFILES_DIR/config/alacritty"
|
|
|
264
264
|
|
|
265
265
|
Each mapping entry supports the following options:
|
|
266
266
|
|
|
267
|
-
|
|
268
|
-
- **`only`**: An array of files or folders. This option ensures that only the specified files or folders from the `src` directory are transferred to the `dest` directory. Example:
|
|
269
|
-
```toml
|
|
270
|
-
[[push.mappings]]
|
|
271
|
-
src = "$XDG_CONFIG_HOME"
|
|
272
|
-
dest = "$DOTFILES_DIR/config"
|
|
273
|
-
only = ["config.yml", "themes"]
|
|
267
|
+
##### `force` Option
|
|
274
268
|
|
|
275
|
-
|
|
276
|
-
|
|
269
|
+
A boolean (true/false) value. When set to `true`, it forces deletion of files in the destination directory that don't exist in the source. This is particularly useful when you need to ensure the destination stays synchronized with the source.
|
|
270
|
+
|
|
271
|
+
**Example:**
|
|
272
|
+
```toml
|
|
273
|
+
[[pull.mappings]]
|
|
274
|
+
src = "$XDG_CONFIG_HOME_MIRROR/nvim"
|
|
275
|
+
dest = "$XDG_CONFIG_HOME/nvim"
|
|
276
|
+
force = true
|
|
277
|
+
ignore = ["lazy-lock.json"]
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
> [!WARNING]
|
|
281
|
+
> When using `force = true` with the `only` option, only files matching the `only` filter will be managed. Other files in the destination remain untouched.
|
|
282
|
+
|
|
283
|
+
##### `only` Option
|
|
284
|
+
|
|
285
|
+
An array of relative paths (files or directories) to selectively transfer from the source. This option provides precise control over which files get synchronized.
|
|
286
|
+
|
|
287
|
+
**How it works:**
|
|
288
|
+
- Paths are relative to the `src` directory
|
|
289
|
+
- You can specify entire directories or individual files
|
|
290
|
+
- Parent directories are automatically created as needed
|
|
291
|
+
- Other files in the source are ignored
|
|
292
|
+
- With `force = true`, only files matching the `only` filter are cleaned up in the destination
|
|
293
|
+
|
|
294
|
+
**Example 1: Selecting specific directories**
|
|
295
|
+
```toml
|
|
296
|
+
[[push.mappings]]
|
|
297
|
+
src = "$XDG_CONFIG_HOME"
|
|
298
|
+
dest = "$DOTFILES_DIR/config"
|
|
299
|
+
only = ["nvim", "alacritty", "zsh"]
|
|
300
|
+
```
|
|
301
|
+
This transfers only the `nvim/`, `alacritty/`, and `zsh/` directories.
|
|
302
|
+
|
|
303
|
+
**Example 2: Selecting specific files**
|
|
304
|
+
```toml
|
|
305
|
+
[[push.mappings]]
|
|
306
|
+
src = "$XDG_CONFIG_HOME/alacritty"
|
|
307
|
+
dest = "$DOTFILES_DIR/config/alacritty"
|
|
308
|
+
only = ["alacritty.toml", "rose-pine.toml"]
|
|
309
|
+
```
|
|
310
|
+
This transfers only two specific TOML files from the alacritty config directory.
|
|
311
|
+
|
|
312
|
+
**Example 3: Selecting files inside nested directories**
|
|
313
|
+
```toml
|
|
314
|
+
[[push.mappings]]
|
|
315
|
+
src = "$HOME/.config"
|
|
316
|
+
dest = "$DOTFILES_DIR/config"
|
|
317
|
+
only = ["bundle/config", "ghc/ghci.conf", "cabal/config"]
|
|
318
|
+
```
|
|
319
|
+
This transfers only specific configuration files from different subdirectories:
|
|
320
|
+
- `bundle/config` file from the `bundle/` directory
|
|
321
|
+
- `ghc/ghci.conf` file from the `ghc/` directory
|
|
322
|
+
- `cabal/config` file from the `cabal/` directory
|
|
323
|
+
|
|
324
|
+
The parent directories (`bundle/`, `ghc/`, `cabal/`) are created automatically in the destination, but other files in those directories are not transferred.
|
|
325
|
+
|
|
326
|
+
**Example 4: Deeply nested paths**
|
|
327
|
+
```toml
|
|
328
|
+
[[push.mappings]]
|
|
329
|
+
src = "$XDG_CONFIG_HOME"
|
|
330
|
+
dest = "$DOTFILES_DIR/config"
|
|
331
|
+
only = ["nvim/lua/plugins/init.lua", "nvim/lua/config/settings.lua"]
|
|
332
|
+
```
|
|
333
|
+
This transfers only specific Lua files from deeply nested paths within the nvim configuration.
|
|
334
|
+
|
|
335
|
+
**Important behaviors:**
|
|
336
|
+
- **File-specific paths**: When specifying individual files (e.g., `"bundle/config"`), only that file is managed. Sibling files in the same directory are not affected, even with `force = true`.
|
|
337
|
+
- **Directory paths**: When specifying directories (e.g., `"nvim"`), all contents of that directory are managed, including subdirectories.
|
|
338
|
+
- **Combining with `force`**: With `force = true` and directory paths, files in the destination directory that don't exist in the source are removed. With file-specific paths, only that specific file is managed.
|
|
339
|
+
|
|
340
|
+
##### `ignore` Option
|
|
341
|
+
|
|
342
|
+
An array of relative paths or patterns to exclude during transfer. This allows you to skip certain files or folders.
|
|
343
|
+
|
|
344
|
+
**Example:**
|
|
345
|
+
```toml
|
|
346
|
+
[[pull.mappings]]
|
|
347
|
+
src = "$XDG_CONFIG_HOME_MIRROR/nvim"
|
|
348
|
+
dest = "$XDG_CONFIG_HOME/nvim"
|
|
349
|
+
ignore = ["lazy-lock.json", "plugin/packer_compiled.lua"]
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
**Combining options:**
|
|
353
|
+
```toml
|
|
354
|
+
[[push.mappings]]
|
|
355
|
+
src = "$XDG_CONFIG_HOME/nvim"
|
|
356
|
+
dest = "$DOTFILES_DIR/config/nvim"
|
|
357
|
+
only = ["lua", "init.lua"]
|
|
358
|
+
ignore = ["lua/plugin/packer_compiled.lua"]
|
|
359
|
+
force = true
|
|
360
|
+
```
|
|
361
|
+
This configuration:
|
|
362
|
+
1. Transfers only the `lua/` directory and `init.lua` file (`only`)
|
|
363
|
+
2. Excludes `lua/plugin/packer_compiled.lua` even though it's in the `lua/` directory (`ignore`)
|
|
364
|
+
3. Removes files in the destination that don't exist in the source (`force`)
|
|
365
|
+
|
|
366
|
+
> [!NOTE]
|
|
367
|
+
> When `ignore` and `only` both match a path, `ignore` takes precedence.
|
|
277
368
|
|
|
278
369
|
These options apply when the source is a directory and are relevant for both `push` and `pull` operations.
|
|
279
370
|
|
data/exe/dotsync
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
require_relative "../lib/dotsync"
|
|
5
4
|
require "optparse"
|
|
5
|
+
require_relative "../lib/dotsync/version"
|
|
6
6
|
|
|
7
7
|
options = { apply: false, config_path: nil }
|
|
8
8
|
|
|
9
|
+
# Initialize logger early for consistent error handling
|
|
10
|
+
require_relative "../lib/dotsync/core"
|
|
11
|
+
logger = Dotsync::Logger.new
|
|
12
|
+
|
|
9
13
|
opt_parser = OptionParser.new do |opts|
|
|
10
14
|
opts.banner = <<~BANNER
|
|
11
15
|
dotsync #{Dotsync::VERSION}
|
|
@@ -30,6 +34,7 @@ opt_parser = OptionParser.new do |opts|
|
|
|
30
34
|
dotsync diff # Show what would change
|
|
31
35
|
dotsync watch # Monitor and sync continuously
|
|
32
36
|
dotsync -c ~/custom.toml push # Use custom config file
|
|
37
|
+
dotsync push --trace # Show full error backtraces
|
|
33
38
|
|
|
34
39
|
Options:
|
|
35
40
|
-a, --apply Apply changes (push or pull)
|
|
@@ -46,6 +51,7 @@ opt_parser = OptionParser.new do |opts|
|
|
|
46
51
|
--only-config Show only the config section
|
|
47
52
|
--only-mappings Show only the mappings section
|
|
48
53
|
-v, --verbose Force showing all available information
|
|
54
|
+
--trace Show full error backtraces (for debugging)
|
|
49
55
|
--version Show version number
|
|
50
56
|
-h, --help Show this help message
|
|
51
57
|
BANNER
|
|
@@ -106,6 +112,10 @@ opt_parser = OptionParser.new do |opts|
|
|
|
106
112
|
options[:verbose] = true
|
|
107
113
|
end
|
|
108
114
|
|
|
115
|
+
opts.on("--trace", "Show full error backtraces (for debugging)") do
|
|
116
|
+
options[:trace] = true
|
|
117
|
+
end
|
|
118
|
+
|
|
109
119
|
opts.on("--version", "Show version number") do
|
|
110
120
|
puts "dotsync #{Dotsync::VERSION}"
|
|
111
121
|
exit
|
|
@@ -117,20 +127,43 @@ opt_parser = OptionParser.new do |opts|
|
|
|
117
127
|
end
|
|
118
128
|
end
|
|
119
129
|
|
|
120
|
-
|
|
130
|
+
begin
|
|
131
|
+
opt_parser.parse!
|
|
132
|
+
rescue OptionParser::InvalidOption, OptionParser::InvalidArgument => e
|
|
133
|
+
# Extract just the problematic option from the error message
|
|
134
|
+
option_match = e.message.match(/invalid (?:option|argument): (.+)/)
|
|
135
|
+
problem = option_match ? option_match[1] : e.message
|
|
136
|
+
|
|
137
|
+
logger.error("Invalid option: #{problem}")
|
|
138
|
+
logger.info("See 'dotsync --help' for available options")
|
|
139
|
+
|
|
140
|
+
if options[:trace]
|
|
141
|
+
logger.log("\nFull backtrace:", color: 240)
|
|
142
|
+
e.backtrace.each { |line| logger.log(" #{line}", color: 240) }
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
exit 1
|
|
146
|
+
end
|
|
121
147
|
|
|
122
148
|
command = ARGV.shift
|
|
123
149
|
|
|
124
|
-
|
|
150
|
+
# Load only the dependencies needed for the specific command
|
|
151
|
+
begin
|
|
152
|
+
case command
|
|
125
153
|
when "push"
|
|
154
|
+
require_relative "../lib/dotsync/loaders/push_loader"
|
|
126
155
|
Dotsync::Runner.new(config_path: options[:config_path]).run(:push, options)
|
|
127
156
|
when "pull"
|
|
157
|
+
require_relative "../lib/dotsync/loaders/pull_loader"
|
|
128
158
|
Dotsync::Runner.new(config_path: options[:config_path]).run(:pull, options)
|
|
129
159
|
when "watch"
|
|
160
|
+
require_relative "../lib/dotsync/loaders/watch_loader"
|
|
130
161
|
Dotsync::Runner.new(config_path: options[:config_path]).run(:watch, options)
|
|
131
162
|
when "setup", "init"
|
|
163
|
+
require_relative "../lib/dotsync/loaders/setup_loader"
|
|
132
164
|
Dotsync::Runner.new(config_path: options[:config_path]).run(:setup)
|
|
133
165
|
when "status"
|
|
166
|
+
require_relative "../lib/dotsync/loaders/push_loader"
|
|
134
167
|
# Show config and mappings without executing any action
|
|
135
168
|
# Use push action with only-config and only-mappings to display info
|
|
136
169
|
status_options = options.merge(
|
|
@@ -141,14 +174,28 @@ when "status"
|
|
|
141
174
|
)
|
|
142
175
|
Dotsync::Runner.new(config_path: options[:config_path]).run(:push, status_options)
|
|
143
176
|
when "diff"
|
|
177
|
+
require_relative "../lib/dotsync/loaders/push_loader"
|
|
144
178
|
# Alias for push in preview mode (default behavior)
|
|
145
179
|
diff_options = options.merge(apply: false)
|
|
146
180
|
Dotsync::Runner.new(config_path: options[:config_path]).run(:push, diff_options)
|
|
147
181
|
else
|
|
148
182
|
if command
|
|
149
|
-
|
|
183
|
+
logger.error("Unknown command: '#{command}'")
|
|
184
|
+
logger.info("See 'dotsync --help' for available commands")
|
|
150
185
|
else
|
|
151
186
|
puts opt_parser.banner
|
|
152
187
|
end
|
|
153
188
|
exit 1
|
|
189
|
+
end
|
|
190
|
+
rescue => e
|
|
191
|
+
logger.error("Unexpected error: #{e.message}")
|
|
192
|
+
|
|
193
|
+
if options[:trace]
|
|
194
|
+
logger.log("\nFull backtrace:", color: 240)
|
|
195
|
+
e.backtrace.each { |line| logger.log(" #{line}", color: 240) }
|
|
196
|
+
else
|
|
197
|
+
logger.info("Run with --trace to see full error details")
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
exit 1
|
|
154
201
|
end
|
|
@@ -8,6 +8,7 @@ module Dotsync
|
|
|
8
8
|
|
|
9
9
|
# Initialize the BaseConfig with the provided path.
|
|
10
10
|
# Loads the TOML configuration file and validates it.
|
|
11
|
+
# Uses ConfigCache for improved performance.
|
|
11
12
|
#
|
|
12
13
|
# @param [String] path The file path to the configuration file.
|
|
13
14
|
def initialize(path = Dotsync.config_path)
|
|
@@ -20,7 +21,7 @@ module Dotsync
|
|
|
20
21
|
" dotsync setup"
|
|
21
22
|
end
|
|
22
23
|
|
|
23
|
-
@config =
|
|
24
|
+
@config = load_config(absolute_path)
|
|
24
25
|
validate!
|
|
25
26
|
end
|
|
26
27
|
|
|
@@ -29,6 +30,15 @@ module Dotsync
|
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
private
|
|
33
|
+
# Loads configuration from file, using cache when possible
|
|
34
|
+
#
|
|
35
|
+
# @param [String] path The file path to the configuration file.
|
|
36
|
+
# @return [Hash] The parsed configuration
|
|
37
|
+
def load_config(path)
|
|
38
|
+
require_relative "../utils/config_cache"
|
|
39
|
+
ConfigCache.new(path).load
|
|
40
|
+
end
|
|
41
|
+
|
|
32
42
|
# Validates the configuration file.
|
|
33
43
|
#
|
|
34
44
|
# @raise [NotImplementedError] if not implemented by a subclass.
|
data/lib/dotsync/core.rb
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Core dependencies needed by all actions
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "logger"
|
|
6
|
+
require "forwardable"
|
|
7
|
+
require "ostruct"
|
|
8
|
+
require "find"
|
|
9
|
+
|
|
10
|
+
# Base classes and utilities
|
|
11
|
+
require_relative "errors"
|
|
12
|
+
require_relative "icons"
|
|
13
|
+
require_relative "colors"
|
|
14
|
+
require_relative "version"
|
|
15
|
+
|
|
16
|
+
# Config Concerns (loaded early as they're used by other modules)
|
|
17
|
+
require_relative "config/concerns/xdg_base_directory"
|
|
18
|
+
|
|
19
|
+
# Utils (common utilities)
|
|
20
|
+
require_relative "utils/path_utils"
|
|
21
|
+
require_relative "utils/logger"
|
|
22
|
+
require_relative "utils/version_checker"
|
|
23
|
+
|
|
24
|
+
# Runner
|
|
25
|
+
require_relative "runner"
|
|
26
|
+
|
|
27
|
+
module Dotsync
|
|
28
|
+
class << self
|
|
29
|
+
attr_writer :config_path
|
|
30
|
+
|
|
31
|
+
def config_path
|
|
32
|
+
@config_path ||= ENV["DOTSYNC_CONFIG"] || "~/.config/dotsync.toml"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Load core dependencies
|
|
4
|
+
require_relative "../core"
|
|
5
|
+
|
|
6
|
+
# Gems needed for pull
|
|
7
|
+
require "toml-rb"
|
|
8
|
+
require "terminal-table"
|
|
9
|
+
|
|
10
|
+
# Utils needed for pull
|
|
11
|
+
require_relative "../utils/file_transfer"
|
|
12
|
+
require_relative "../utils/directory_differ"
|
|
13
|
+
require_relative "../utils/config_cache"
|
|
14
|
+
|
|
15
|
+
# Models
|
|
16
|
+
require_relative "../models/mapping"
|
|
17
|
+
require_relative "../models/diff"
|
|
18
|
+
|
|
19
|
+
# Config
|
|
20
|
+
require_relative "../config/base_config"
|
|
21
|
+
require_relative "../config/pull_action_config"
|
|
22
|
+
|
|
23
|
+
# Actions Concerns
|
|
24
|
+
require_relative "../actions/concerns/mappings_transfer"
|
|
25
|
+
require_relative "../actions/concerns/output_sections"
|
|
26
|
+
|
|
27
|
+
# Actions
|
|
28
|
+
require_relative "../actions/base_action"
|
|
29
|
+
require_relative "../actions/pull_action"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Load core dependencies
|
|
4
|
+
require_relative "../core"
|
|
5
|
+
|
|
6
|
+
# Gems needed for push
|
|
7
|
+
require "toml-rb"
|
|
8
|
+
require "terminal-table"
|
|
9
|
+
|
|
10
|
+
# Utils needed for push
|
|
11
|
+
require_relative "../utils/file_transfer"
|
|
12
|
+
require_relative "../utils/directory_differ"
|
|
13
|
+
require_relative "../utils/config_cache"
|
|
14
|
+
|
|
15
|
+
# Models
|
|
16
|
+
require_relative "../models/mapping"
|
|
17
|
+
require_relative "../models/diff"
|
|
18
|
+
|
|
19
|
+
# Config
|
|
20
|
+
require_relative "../config/base_config"
|
|
21
|
+
require_relative "../config/push_action_config"
|
|
22
|
+
|
|
23
|
+
# Actions Concerns
|
|
24
|
+
require_relative "../actions/concerns/mappings_transfer"
|
|
25
|
+
require_relative "../actions/concerns/output_sections"
|
|
26
|
+
|
|
27
|
+
# Actions
|
|
28
|
+
require_relative "../actions/base_action"
|
|
29
|
+
require_relative "../actions/push_action"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Setup only needs minimal dependencies
|
|
4
|
+
require "toml-rb"
|
|
5
|
+
require "fileutils"
|
|
6
|
+
|
|
7
|
+
# Load only what's needed
|
|
8
|
+
require_relative "../version"
|
|
9
|
+
require_relative "../utils/logger"
|
|
10
|
+
require_relative "../runner"
|
|
11
|
+
|
|
12
|
+
module Dotsync
|
|
13
|
+
class << self
|
|
14
|
+
attr_writer :config_path
|
|
15
|
+
|
|
16
|
+
def config_path
|
|
17
|
+
@config_path ||= ENV["DOTSYNC_CONFIG"] || "~/.config/dotsync.toml"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Load core dependencies
|
|
4
|
+
require_relative "../core"
|
|
5
|
+
|
|
6
|
+
# Gems needed for watch (includes listen)
|
|
7
|
+
require "listen"
|
|
8
|
+
require "toml-rb"
|
|
9
|
+
|
|
10
|
+
# Utils needed for watch
|
|
11
|
+
require_relative "../utils/file_transfer"
|
|
12
|
+
require_relative "../utils/config_cache"
|
|
13
|
+
|
|
14
|
+
# Models
|
|
15
|
+
require_relative "../models/mapping"
|
|
16
|
+
|
|
17
|
+
# Config
|
|
18
|
+
require_relative "../config/base_config"
|
|
19
|
+
require_relative "../config/watch_action_config"
|
|
20
|
+
|
|
21
|
+
# Actions Concerns
|
|
22
|
+
require_relative "../actions/concerns/mappings_transfer"
|
|
23
|
+
|
|
24
|
+
# Actions
|
|
25
|
+
require_relative "../actions/base_action"
|
|
26
|
+
require_relative "../actions/watch_action"
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "digest"
|
|
5
|
+
|
|
6
|
+
module Dotsync
|
|
7
|
+
class ConfigCache
|
|
8
|
+
include Dotsync::XDGBaseDirectory
|
|
9
|
+
|
|
10
|
+
def initialize(config_path)
|
|
11
|
+
@config_path = File.expand_path(config_path)
|
|
12
|
+
@cache_dir = File.join(xdg_data_home, "dotsync", "config_cache")
|
|
13
|
+
|
|
14
|
+
# Use hash of real path for cache filename to support multiple configs
|
|
15
|
+
cache_key = Digest::SHA256.hexdigest(File.realpath(@config_path))
|
|
16
|
+
@cache_file = File.join(@cache_dir, "#{cache_key}.cache")
|
|
17
|
+
@meta_file = File.join(@cache_dir, "#{cache_key}.meta")
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def load
|
|
21
|
+
# Skip cache if disabled via environment variable
|
|
22
|
+
return parse_toml if ENV["DOTSYNC_NO_CACHE"]
|
|
23
|
+
|
|
24
|
+
return parse_and_cache unless valid_cache?
|
|
25
|
+
|
|
26
|
+
# Fast path: load from cache
|
|
27
|
+
Marshal.load(File.binread(@cache_file))
|
|
28
|
+
rescue StandardError
|
|
29
|
+
# Fallback: reparse if cache corrupted or any error
|
|
30
|
+
parse_and_cache
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
def valid_cache?
|
|
35
|
+
return false unless File.exist?(@cache_file)
|
|
36
|
+
return false unless File.exist?(@meta_file)
|
|
37
|
+
|
|
38
|
+
meta = JSON.parse(File.read(@meta_file))
|
|
39
|
+
source_stat = File.stat(@config_path)
|
|
40
|
+
|
|
41
|
+
# Quick validation checks
|
|
42
|
+
return false if source_stat.mtime.to_f != meta["source_mtime"]
|
|
43
|
+
return false if source_stat.size != meta["source_size"]
|
|
44
|
+
return false if Dotsync::VERSION != meta["dotsync_version"]
|
|
45
|
+
|
|
46
|
+
# Age check (invalidate cache older than 7 days for safety)
|
|
47
|
+
cache_age_days = (Time.now.to_f - meta["cached_at"]) / 86400
|
|
48
|
+
return false if cache_age_days > 7
|
|
49
|
+
|
|
50
|
+
true
|
|
51
|
+
rescue StandardError
|
|
52
|
+
# Any error in validation means invalid cache
|
|
53
|
+
false
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def parse_and_cache
|
|
57
|
+
config = parse_toml
|
|
58
|
+
|
|
59
|
+
# Write cache files
|
|
60
|
+
FileUtils.mkdir_p(@cache_dir)
|
|
61
|
+
File.binwrite(@cache_file, Marshal.dump(config))
|
|
62
|
+
File.write(@meta_file, JSON.generate(build_metadata))
|
|
63
|
+
|
|
64
|
+
config
|
|
65
|
+
rescue StandardError
|
|
66
|
+
# If caching fails, still return the parsed config
|
|
67
|
+
config
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def parse_toml
|
|
71
|
+
require "toml-rb"
|
|
72
|
+
TomlRB.load_file(@config_path)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def build_metadata
|
|
76
|
+
source_stat = File.stat(@config_path)
|
|
77
|
+
{
|
|
78
|
+
source_path: @config_path,
|
|
79
|
+
source_size: source_stat.size,
|
|
80
|
+
source_mtime: source_stat.mtime.to_f,
|
|
81
|
+
cached_at: Time.now.to_f,
|
|
82
|
+
dotsync_version: Dotsync::VERSION
|
|
83
|
+
}
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
data/lib/dotsync/version.rb
CHANGED
data/lib/dotsync.rb
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# Full library load for backward compatibility
|
|
4
|
+
# For optimized loading, use specific loaders in lib/dotsync/loaders/
|
|
5
|
+
|
|
3
6
|
# Libs dependencies
|
|
4
7
|
require "fileutils"
|
|
5
8
|
require "listen"
|
|
@@ -26,6 +29,7 @@ require_relative "dotsync/utils/logger"
|
|
|
26
29
|
require_relative "dotsync/utils/file_transfer"
|
|
27
30
|
require_relative "dotsync/utils/directory_differ"
|
|
28
31
|
require_relative "dotsync/utils/version_checker"
|
|
32
|
+
require_relative "dotsync/utils/config_cache"
|
|
29
33
|
|
|
30
34
|
# Models
|
|
31
35
|
require_relative "dotsync/models/mapping"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dotsync
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.25
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Sáenz
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-11-
|
|
11
|
+
date: 2025-11-16 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: toml-rb
|
|
@@ -321,12 +321,18 @@ files:
|
|
|
321
321
|
- lib/dotsync/config/pull_action_config.rb
|
|
322
322
|
- lib/dotsync/config/push_action_config.rb
|
|
323
323
|
- lib/dotsync/config/watch_action_config.rb
|
|
324
|
+
- lib/dotsync/core.rb
|
|
324
325
|
- lib/dotsync/errors.rb
|
|
325
326
|
- lib/dotsync/icons.rb
|
|
327
|
+
- lib/dotsync/loaders/pull_loader.rb
|
|
328
|
+
- lib/dotsync/loaders/push_loader.rb
|
|
329
|
+
- lib/dotsync/loaders/setup_loader.rb
|
|
330
|
+
- lib/dotsync/loaders/watch_loader.rb
|
|
326
331
|
- lib/dotsync/models/diff.rb
|
|
327
332
|
- lib/dotsync/models/mapping.rb
|
|
328
333
|
- lib/dotsync/runner.rb
|
|
329
334
|
- lib/dotsync/tasks/actions.rake
|
|
335
|
+
- lib/dotsync/utils/config_cache.rb
|
|
330
336
|
- lib/dotsync/utils/directory_differ.rb
|
|
331
337
|
- lib/dotsync/utils/file_transfer.rb
|
|
332
338
|
- lib/dotsync/utils/logger.rb
|