dotsync 0.1.20 → 0.1.22

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: b3c840b83736169eaabf3296862872f088fc9ae49dc15545adf49bf75439620e
4
- data.tar.gz: c8cf364077464a687df689c9bc6f43aa75fa45c61df531fcdc498a043ab2b02d
3
+ metadata.gz: f1bb84e77b3093d786b7f502618d7b7ab060deb8b2ea8b0df2a7c3362690f017
4
+ data.tar.gz: 21a4b2e7b192d0348c900fd5481939b16cdf7e48ecb27780bbd1b20695133ef5
5
5
  SHA512:
6
- metadata.gz: 105b36727336e5169a23de08a2a8f2165a568682a8ea4f590f93d93525990181cf3994239151d9a1e72d75e33416c217777a604373a37fbd0d7ff3e1aca94d93
7
- data.tar.gz: 97a767742387ebd6af332e8e1a1d4e19b887c019fe5fb4863d449ea9e9ba6c9ddcf8459bee0a018362761d7f2c4d699bd7b69240af45ebc144c9ded697d46115
6
+ metadata.gz: 2bb46cecd31107adca67df98262c21a646c28089f3f3ab63be668119cbc4231d8af5341433fca6b0a848235940aa0a4c223e004db0054ae7b7770f1ebaa17c7b
7
+ data.tar.gz: 4e6de7cc7c19cbe5408d689f49e4bd565f4970cbc673c1692313114021046ffaf9fd57efb8fd7446001b9e32a4999eb23a1a6410414fa52a0429b91d43b89b11
data/.editorconfig ADDED
@@ -0,0 +1,42 @@
1
+ # EditorConfig helps maintain consistent coding styles across editors and IDEs
2
+ # https://editorconfig.org
3
+
4
+ root = true
5
+
6
+ # Default settings for all files
7
+ [*]
8
+ charset = utf-8
9
+ end_of_line = lf
10
+ insert_final_newline = true
11
+ trim_trailing_whitespace = true
12
+
13
+ # Ruby files
14
+ [*.{rb,rake}]
15
+ indent_style = space
16
+ indent_size = 2
17
+
18
+ # YAML files
19
+ [*.{yml,yaml}]
20
+ indent_style = space
21
+ indent_size = 2
22
+
23
+ # Markdown files
24
+ [*.md]
25
+ indent_style = space
26
+ indent_size = 2
27
+ trim_trailing_whitespace = false
28
+
29
+ # Configuration files
30
+ [*.{toml,json}]
31
+ indent_style = space
32
+ indent_size = 2
33
+
34
+ # Shell scripts
35
+ [*.sh]
36
+ indent_style = space
37
+ indent_size = 2
38
+
39
+ # Gemfile and other Ruby config files
40
+ [{Gemfile,Rakefile,*.gemspec}]
41
+ indent_style = space
42
+ indent_size = 2
@@ -29,10 +29,32 @@ jobs:
29
29
  run: |
30
30
  bundle exec rake
31
31
 
32
+ - name: Display coverage summary
33
+ if: always()
34
+ run: |
35
+ echo "📊 SimpleCov Coverage Report:"
36
+ if [ -f coverage/.last_run.json ]; then
37
+ ruby -rjson -e "data=JSON.parse(File.read('coverage/.last_run.json')); puts \" Line Coverage: #{data['result']['line']}%\"; puts \" Branch Coverage: #{data['result']['branch']}%\""
38
+ else
39
+ echo " ⚠️ Coverage report not found"
40
+ fi
41
+
42
+ - name: Upload coverage report
43
+ uses: actions/upload-artifact@v4
44
+ if: always()
45
+ with:
46
+ name: coverage-report-ruby-${{ matrix.ruby }}
47
+ path: coverage/
48
+ retention-days: 30
49
+
32
50
  - name: Run RuboCop
33
51
  run: |
34
52
  bundle exec rubocop
35
53
 
54
+ - name: Run security audit
55
+ run: |
56
+ bundle exec bundle-audit check --update
57
+
36
58
  - name: Publish to RubyGems
37
59
  if: matrix.ruby == '3.2' && github.ref_name == 'master'
38
60
  run: |
data/CHANGELOG.md CHANGED
@@ -1,3 +1,64 @@
1
+ # 0.1.22
2
+
3
+ **Testing & Quality:**
4
+ - Increase test coverage from 89.03% to 96.13% line coverage (+7.1%)
5
+ - Increase branch coverage from 75.0% to 81.14% (+6.1%)
6
+ - Add comprehensive tests for Runner, Colors, OutputSections, XDGBaseDirectory
7
+ - Add error handling and confirmation prompt tests for PullAction and PushAction
8
+ - Suppress print output during tests for clean terminal output
9
+ - Update SimpleCov thresholds to 95% line / 80% branch
10
+
11
+ **Bug Fixes:**
12
+ - Fix XDGBaseDirectory path expansion to use ~ instead of $HOME literal
13
+ - Fix Colors accessor methods using wrong hash keys
14
+ - Fix OutputSections to hide differences_legend when only_mappings option is true
15
+ - Add nil config protection in Colors.load_custom_colors
16
+
17
+ **CI/CD:**
18
+ - Add SimpleCov coverage reporting to GitHub Actions workflow
19
+ - Display coverage summary in CI logs (line and branch percentages)
20
+ - Upload coverage HTML reports as artifacts with 30-day retention
21
+
22
+ **New Test Files:**
23
+ - spec/dotsync/runner_spec.rb (24 examples)
24
+ - spec/dotsync/colors_spec.rb (14 examples)
25
+ - spec/dotsync/actions/concerns/output_sections_spec.rb (11 examples)
26
+ - spec/dotsync/config/concerns/xdg_base_directory_spec.rb (8 examples)
27
+
28
+ Total: 323 examples, 0 failures, 2 pending
29
+
30
+ # 0.1.21
31
+
32
+ **New Features & Commands:**
33
+ - Add `status` command to show current configuration and mappings without executing actions
34
+ - Add `diff` command as convenient alias for preview mode (push without --apply)
35
+ - Add `init` command as alias for `setup` command
36
+ - Add `--version` flag to display version number
37
+ - Add `--dry-run` flag as explicit alias for preview mode (industry-standard terminology)
38
+ - Add `-y, --yes` flag to skip confirmation prompts for automation and scripting
39
+ - Add `-c, --config PATH` flag to specify custom config file path (enables multiple config workflows)
40
+
41
+ **Safety & User Experience:**
42
+ - Add confirmation prompt before applying changes showing file count and requiring explicit consent
43
+ - Confirmation can be bypassed with `--yes` flag or skipped in `--quiet` mode
44
+ - Improve error messages with actionable guidance for common issues (permissions, disk space, symlinks, type conflicts)
45
+ - Errors are now handled per-mapping, allowing processing to continue for other mappings
46
+ - Watch command now accepts and respects CLI options (--quiet, --no-legend, --no-mappings)
47
+
48
+ **Documentation:**
49
+ - Complete README overhaul documenting all CLI flags and command aliases
50
+ - Add comprehensive "Safety Features" section covering confirmation prompts, backups, and error handling
51
+ - Add "Command Options" section with all flags organized by category
52
+ - Add "Examples" section with 20+ practical usage patterns
53
+ - Enhance "Pro Tips" with guidance on multiple configs, automation, and command aliases
54
+ - Expand "Troubleshooting" section with solutions for confirmation prompts and config file management
55
+ - Update Table of Contents to include Safety Features section
56
+
57
+ **Developer Experience:**
58
+ - Add practical examples to CLI help text showing common usage patterns
59
+ - Improve help text organization with clearer command descriptions
60
+ - Better error handling for missing config file with actionable suggestions
61
+
1
62
  # 0.1.20
2
63
 
3
64
  **Robustness & Error Handling:**
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dotsync (0.1.20)
4
+ dotsync (0.1.22)
5
5
  fileutils (~> 1.7.3)
6
6
  find (~> 0.2.0)
7
7
  listen (~> 3.9.0)
@@ -14,12 +14,16 @@ GEM
14
14
  remote: https://rubygems.org/
15
15
  specs:
16
16
  ast (2.4.3)
17
+ bundler-audit (0.9.2)
18
+ bundler (>= 1.2.0, < 3)
19
+ thor (~> 1.0)
17
20
  citrus (3.0.2)
18
21
  date (3.5.0)
19
22
  debug (1.11.0)
20
23
  irb (~> 1.10)
21
24
  reline (>= 0.3.8)
22
25
  diff-lcs (1.6.2)
26
+ docile (1.4.1)
23
27
  erb (5.1.3)
24
28
  ffi (1.17.2)
25
29
  ffi (1.17.2-aarch64-linux-gnu)
@@ -118,9 +122,16 @@ GEM
118
122
  prism (>= 1.2, < 2.0)
119
123
  rbs (>= 3, < 5)
120
124
  ruby-progressbar (1.13.0)
125
+ simplecov (0.22.0)
126
+ docile (~> 1.1)
127
+ simplecov-html (~> 0.11)
128
+ simplecov_json_formatter (~> 0.1)
129
+ simplecov-html (0.13.2)
130
+ simplecov_json_formatter (0.1.4)
121
131
  stringio (3.1.7)
122
132
  terminal-table (4.0.0)
123
133
  unicode-display_width (>= 1.1.1, < 4)
134
+ thor (1.4.0)
124
135
  timecop (0.9.10)
125
136
  toml-rb (4.0.0)
126
137
  citrus (~> 3.0, > 3.0)
@@ -144,6 +155,7 @@ PLATFORMS
144
155
  x86_64-linux-musl
145
156
 
146
157
  DEPENDENCIES
158
+ bundler-audit (~> 0.9.0)
147
159
  debug (~> 1.11)
148
160
  dotsync!
149
161
  rake (~> 13.3.0)
@@ -154,6 +166,7 @@ DEPENDENCIES
154
166
  rubocop-rake (~> 0.7.1)
155
167
  rubocop-rspec (~> 3.7.0)
156
168
  ruby-lsp (~> 0.26.1)
169
+ simplecov (~> 0.22.0)
157
170
  timecop (~> 0.9.10)
158
171
 
159
172
  BUNDLED WITH
data/README.md CHANGED
@@ -29,6 +29,7 @@ Dotsync is a powerful Ruby gem for managing and synchronizing your dotfiles acro
29
29
  - [Usage](#usage)
30
30
  - [Executable Commands](#executable-commands)
31
31
  - [Configuration](#configuration)
32
+ - [Safety Features](#safety-features)
32
33
  - [Customizing Icons](#customizing-icons)
33
34
  - [Automatic Update Checks](#automatic-update-checks)
34
35
  - [Pro Tips](#pro-tips)
@@ -102,39 +103,21 @@ Dotsync provides the following commands to manage your dotfiles:
102
103
  > [!IMPORTANT]
103
104
  > By default, both `push` and `pull` commands run in **preview mode** (dry-run). They will show you what changes would be made without actually modifying any files. To apply changes, you **must** use the `--apply` flag.
104
105
 
106
+ #### Core Commands
107
+
105
108
  - **Push**: Transfer dotfiles from your local machine to the destination repository.
106
109
  ```shell
107
- dotsync push --apply [OPTION]
110
+ dotsync push [OPTIONS]
111
+ dotsync push --apply [OPTIONS] # Apply changes
108
112
  ```
109
- Options:
110
- - `-q, --quiet`: Hide all non-essential output (only errors or final status).
111
- - `--no-legend`: Hide all legends for config, mappings, and differences.
112
- - `--no-config`: Hide the config section in the output.
113
- - `--no-mappings`: Hide the mappings and their legend.
114
- - `--no-diff-legend`: Hide the differences legend only.
115
- - `--no-diff`: Hide the differences section itself.
116
- - `--only-diff`: Show only the differences section.
117
- - `--only-config`: Show only the config section.
118
- - `--only-mappings`: Show only the mappings section.
119
- - `-v, --verbose`: Force showing all available information.
120
113
 
121
114
  ![dotsync push](docs/images/dotsync_push.png)
122
115
 
123
116
  - **Pull**: Synchronize dotfiles from the repository to your local machine.
124
117
  ```shell
125
- dotsync pull --apply [OPTION]
118
+ dotsync pull [OPTIONS]
119
+ dotsync pull --apply [OPTIONS] # Apply changes
126
120
  ```
127
- Options:
128
- - `-q, --quiet`: Hide all non-essential output (only errors or final status).
129
- - `--no-legend`: Hide all legends for config, mappings, and differences.
130
- - `--no-config`: Hide the config section in the output.
131
- - `--no-mappings`: Hide the mappings and their legend.
132
- - `--no-diff-legend`: Hide the differences legend only.
133
- - `--no-diff`: Hide the differences section itself.
134
- - `--only-diff`: Show only the differences section.
135
- - `--only-config`: Show only the config section.
136
- - `--only-mappings`: Show only the mappings section.
137
- - `-v, --verbose`: Force showing all available information.
138
121
 
139
122
  During the `pull` operation, `Dotsync::PullAction` creates a backup of the existing files on the destination. These backups are stored in a directory under the XDG path, with each backup organized by a timestamp. To prevent excessive storage usage, only the 10 most recent backups are retained. Older backups are automatically purged, ensuring efficient storage management.
140
123
 
@@ -142,13 +125,90 @@ Dotsync provides the following commands to manage your dotfiles:
142
125
 
143
126
  - **Watch**: Continuously monitor and sync changes between your local machine and the repository.
144
127
  ```shell
145
- dotsync watch
128
+ dotsync watch [OPTIONS]
146
129
  ```
130
+
131
+ The watch command supports the same output control options as push and pull (e.g., `--quiet`, `--no-legend`, `--no-mappings`).
147
132
 
148
- - **Setup**: Generate a default configuration file at `~/.config/dotsync.toml` with example mappings for `pull`, `push`, and `watch`.
133
+ - **Setup** (alias: **init**): Generate a default configuration file at `~/.config/dotsync.toml` with example mappings for `pull`, `push`, and `watch`.
149
134
  ```shell
150
135
  dotsync setup
136
+ dotsync init # Alias for setup
137
+ ```
138
+
139
+ #### Utility Commands
140
+
141
+ - **Status**: Display current configuration and mappings without executing any actions.
142
+ ```shell
143
+ dotsync status
144
+ ```
145
+ This is useful for inspecting your configuration and verifying mappings are correct.
146
+
147
+ - **Diff**: Show differences that would be made (alias for `push` in preview mode).
148
+ ```shell
149
+ dotsync diff
151
150
  ```
151
+ Convenient shorthand for previewing changes without typing `--dry-run`.
152
+
153
+ #### Command Options
154
+
155
+ All push and pull commands support the following options:
156
+
157
+ **Action Control:**
158
+ - `-a, --apply`: Apply changes (without this, commands run in preview mode)
159
+ - `--dry-run`: Explicitly run in preview mode without applying changes (default behavior)
160
+ - `-y, --yes`: Skip confirmation prompt and auto-confirm changes
161
+ - `-c, --config PATH`: Specify a custom config file path (enables multiple config workflows)
162
+
163
+ **Output Control:**
164
+ - `-q, --quiet`: Hide all non-essential output (only errors or final status)
165
+ - `--no-legend`: Hide all legends for config, mappings, and differences
166
+ - `--no-config`: Hide the config section in the output
167
+ - `--no-mappings`: Hide the mappings and their legend
168
+ - `--no-diff-legend`: Hide the differences legend only
169
+ - `--no-diff`: Hide the differences section itself
170
+ - `--only-diff`: Show only the differences section
171
+ - `--only-config`: Show only the config section
172
+ - `--only-mappings`: Show only the mappings section
173
+ - `-v, --verbose`: Force showing all available information
174
+
175
+ **General:**
176
+ - `--version`: Display version number
177
+ - `-h, --help`: Show help message
178
+
179
+ #### Examples
180
+
181
+ ```shell
182
+ # Setup and configuration
183
+ dotsync setup # Create initial config file
184
+ dotsync init # Same as setup (alias)
185
+ dotsync status # View current configuration
186
+
187
+ # Preview changes (dry-run mode)
188
+ dotsync push # Preview push changes
189
+ dotsync pull # Preview pull changes
190
+ dotsync diff # Quick preview (alias for push)
191
+ dotsync push --dry-run # Explicit dry-run flag
192
+
193
+ # Apply changes
194
+ dotsync push --apply # Apply changes with confirmation
195
+ dotsync pull --apply # Apply changes with confirmation
196
+ dotsync push -ay # Apply without confirmation (--apply + --yes)
197
+ dotsync pull --apply --yes # Apply without confirmation
198
+
199
+ # Custom configuration files
200
+ dotsync -c ~/work-dotfiles.toml push # Use work config
201
+ dotsync --config ~/.config/personal.toml pull # Use personal config
202
+
203
+ # Output control
204
+ dotsync pull --quiet # Minimal output
205
+ dotsync push --only-diff # Show only differences
206
+ dotsync pull --apply --yes -q # Silent apply for scripts
207
+
208
+ # Monitoring
209
+ dotsync watch # Watch with default output
210
+ dotsync watch --quiet # Watch with minimal output
211
+ ```
152
212
 
153
213
  ### Configuration
154
214
 
@@ -217,6 +277,98 @@ Each mapping entry supports the following options:
217
277
 
218
278
  These options apply when the source is a directory and are relevant for both `push` and `pull` operations.
219
279
 
280
+ ### Safety Features
281
+
282
+ Dotsync includes several safety mechanisms to prevent accidental data loss:
283
+
284
+ #### Confirmation Prompts
285
+
286
+ Before applying any changes with the `--apply` flag, Dotsync will:
287
+ 1. Show you all differences that will be applied
288
+ 2. Display the total count of files to be modified
289
+ 3. Ask for explicit confirmation: `About to modify X file(s). Continue? [y/N]`
290
+ 4. Only proceed if you type `y` and press Enter
291
+
292
+ **Example:**
293
+ ```shell
294
+ $ dotsync push --apply
295
+
296
+ # ... shows differences ...
297
+
298
+ About to modify 15 file(s).
299
+ Continue? [y/N] y
300
+ ```
301
+
302
+ **Bypassing Confirmation:**
303
+ - Use the `--yes` or `-y` flag to skip confirmation (useful for automation):
304
+ ```shell
305
+ dotsync push --apply --yes
306
+ dotsync pull -ay # Short form
307
+ ```
308
+ - Use the `--quiet` flag (automatically skips prompt and suppresses output)
309
+
310
+ > [!NOTE]
311
+ > No confirmation is shown if there are no differences to apply.
312
+
313
+ #### Automatic Backups
314
+
315
+ When using `pull --apply`, Dotsync automatically:
316
+ - Creates timestamped backups of existing files before overwriting them
317
+ - Stores backups in `~/.cache/dotsync/backups/YYYYMMDDHHMMSS/`
318
+ - Retains only the 10 most recent backups (older ones are purged)
319
+ - Creates backups only when there are actual differences
320
+
321
+ To restore from a backup:
322
+ ```shell
323
+ ls -la ~/.cache/dotsync/backups/
324
+ cp -r ~/.cache/dotsync/backups/20250110143022/* ~/.config/
325
+ ```
326
+
327
+ #### Preview Mode (Dry-Run)
328
+
329
+ By default, all `push` and `pull` commands run in preview mode:
330
+ - Shows exactly what would change without modifying files
331
+ - Must explicitly use `--apply` flag to make changes
332
+ - Use `--dry-run` flag for explicit clarity in scripts
333
+
334
+ #### Enhanced Error Handling
335
+
336
+ Dotsync provides clear, actionable error messages for common issues:
337
+
338
+ - **Permission Errors**:
339
+ ```
340
+ Permission denied: /path/to/file
341
+ Try: chmod +w <path> or check file permissions
342
+ ```
343
+
344
+ - **Disk Full Errors**:
345
+ ```
346
+ Disk full: No space left on device
347
+ Free up disk space and try again
348
+ ```
349
+
350
+ - **Symlink Errors**:
351
+ ```
352
+ Symlink error: Target does not exist
353
+ Check that symlink target exists and is accessible
354
+ ```
355
+
356
+ - **Type Conflicts**:
357
+ ```
358
+ Type conflict: Cannot overwrite directory with file
359
+ Cannot overwrite directory with file or vice versa
360
+ ```
361
+
362
+ Errors are reported per-mapping, allowing Dotsync to continue processing other mappings even if one fails.
363
+
364
+ #### Symlink Support
365
+
366
+ Dotsync properly handles symbolic links:
367
+ - Preserves symlink targets (absolute and relative paths)
368
+ - Handles broken symlinks gracefully
369
+ - Detects type conflicts (e.g., file vs. directory vs. symlink)
370
+ - Provides clear error messages for symlink-related issues
371
+
220
372
  ### Customizing Icons
221
373
 
222
374
  Dotsync allows you to customize the icons displayed in the console output by adding an `[icons]` section to your configuration file (`~/.config/dotsync.toml`). This is useful if you prefer different icons or need compatibility with terminals that don't support Nerd Fonts.
@@ -294,9 +446,36 @@ The check runs after your command completes and uses a cached timestamp to avoid
294
446
  - **Preview Before Applying**: Always run commands without `--apply` first to preview changes:
295
447
  ```shell
296
448
  dotsync pull # Preview changes
449
+ dotsync diff # Quick preview (alias)
297
450
  dotsync pull --apply # Apply after reviewing
298
451
  ```
299
452
 
453
+ - **Check Configuration**: Use the `status` command to inspect your configuration without executing any actions:
454
+ ```shell
455
+ dotsync status # View config and mappings
456
+ ```
457
+
458
+ - **Multiple Config Files**: Use the `-c` flag to maintain separate configurations for different workflows:
459
+ ```bash
460
+ # Work dotfiles
461
+ dotsync -c ~/work-dotfiles.toml push --apply
462
+
463
+ # Personal dotfiles
464
+ dotsync -c ~/.config/personal.toml pull --apply
465
+
466
+ # Server configs
467
+ dotsync --config ~/server.toml push --apply
468
+ ```
469
+
470
+ - **Automation and Scripting**: Use `--yes` flag to skip confirmation prompts:
471
+ ```shell
472
+ # In a script or CI/CD pipeline
473
+ dotsync pull --apply --yes --quiet
474
+
475
+ # Shorthand
476
+ dotsync push -ayq
477
+ ```
478
+
300
479
  - **Using Environment Variables**: Simplify your configuration with mirror environment variables:
301
480
  ```bash
302
481
  # Add to your ~/.zshrc or ~/.bashrc
@@ -317,6 +496,11 @@ The check runs after your command completes and uses a cached timestamp to avoid
317
496
  gem install dotsync
318
497
  ```
319
498
 
499
+ - **Check Version**: Quickly check which version you're running:
500
+ ```shell
501
+ dotsync --version
502
+ ```
503
+
320
504
  - **Disable Update Checks**: If you prefer not to see update notifications:
321
505
  ```shell
322
506
  export DOTSYNC_NO_UPDATE_CHECK=1
@@ -398,8 +582,11 @@ ignore = ["nvim", "cache", "*.log"]
398
582
  **Solution**: Remember to use the `--apply` flag to apply changes. Without it, commands run in preview mode:
399
583
  ```shell
400
584
  dotsync pull --apply
585
+ dotsync push --apply
401
586
  ```
402
587
 
588
+ You can also use the explicit `--dry-run` flag to make preview mode clear in scripts.
589
+
403
590
  ### Permission Denied Errors
404
591
 
405
592
  **Problem**: Getting permission errors when syncing files.
@@ -438,6 +625,48 @@ cp -r ~/.cache/dotsync/backups/YYYYMMDD_HHMMSS/* ~/.config/
438
625
  - Ensure the source directories exist and are accessible
439
626
  - Try stopping and restarting the watch command
440
627
 
628
+ ### Confirmation Prompt Appearing
629
+
630
+ **Problem**: Being prompted to confirm changes when running in scripts or automation.
631
+
632
+ **Solution**: Use the `--yes` or `-y` flag to skip confirmation prompts:
633
+ ```shell
634
+ dotsync push --apply --yes
635
+ dotsync pull -ay # Shorthand
636
+ ```
637
+
638
+ For completely silent operation in scripts:
639
+ ```shell
640
+ dotsync push --apply --yes --quiet
641
+ ```
642
+
643
+ ### Using Multiple Config Files
644
+
645
+ **Problem**: Need different dotfile configurations for work, personal, or different machines.
646
+
647
+ **Solution**: Use the `--config` or `-c` flag to specify custom config files:
648
+ ```shell
649
+ dotsync -c ~/work-dotfiles.toml push
650
+ dotsync --config ~/.config/personal.toml pull
651
+ ```
652
+
653
+ You can maintain separate config files for different environments and switch between them easily.
654
+
655
+ ### Config File Not Found
656
+
657
+ **Problem**: Error message about missing config file.
658
+
659
+ **Solution**: Create a config file using the setup command:
660
+ ```shell
661
+ dotsync setup # Creates ~/.config/dotsync.toml
662
+ dotsync init # Alias for setup
663
+ ```
664
+
665
+ Or specify a custom config file path:
666
+ ```shell
667
+ dotsync -c ~/my-config.toml setup
668
+ ```
669
+
441
670
  ## Development
442
671
 
443
672
  - After checking out the repo, run `bin/setup` to install dependencies.
data/dotsync.gemspec CHANGED
@@ -47,4 +47,6 @@ Gem::Specification.new do |spec|
47
47
  spec.add_development_dependency "rubocop-md", "~> 2.0.3"
48
48
  spec.add_development_dependency "timecop", "~> 0.9.10"
49
49
  spec.add_development_dependency "ruby-lsp", "~> 0.26.1"
50
+ spec.add_development_dependency "simplecov", "~> 0.22.0"
51
+ spec.add_development_dependency "bundler-audit", "~> 0.9.0"
50
52
  end
data/exe/dotsync CHANGED
@@ -4,7 +4,7 @@
4
4
  require_relative "../lib/dotsync"
5
5
  require "optparse"
6
6
 
7
- options = { apply: false }
7
+ options = { apply: false, config_path: nil }
8
8
 
9
9
  opt_parser = OptionParser.new do |opts|
10
10
  opts.banner = <<~BANNER
@@ -12,13 +12,30 @@ opt_parser = OptionParser.new do |opts|
12
12
  Usage: dotsync [command] [options]
13
13
 
14
14
  Commands:
15
- push Upload local changes to the remote
16
- pull Download remote changes to the local
17
- watch Continuously monitor and sync changes
18
- setup Initialize a default configuration file
15
+ push Upload local changes to the remote
16
+ pull Download remote changes to the local
17
+ watch Continuously monitor and sync changes
18
+ setup Initialize a default configuration file
19
+ init Alias for 'setup'
20
+ status Show current configuration and mappings
21
+ diff Show differences (alias for 'push' without --apply)
22
+
23
+ Examples:
24
+ dotsync setup # Create initial config file
25
+ dotsync push # Preview changes (dry-run)
26
+ dotsync push --apply # Apply changes to remote
27
+ dotsync push -ay # Apply without confirmation
28
+ dotsync pull --quiet # Pull with minimal output
29
+ dotsync status # Show current setup
30
+ dotsync diff # Show what would change
31
+ dotsync watch # Monitor and sync continuously
32
+ dotsync -c ~/custom.toml push # Use custom config file
19
33
 
20
34
  Options:
21
35
  -a, --apply Apply changes (push or pull)
36
+ --dry-run Preview changes without applying (default behavior)
37
+ -y, --yes Skip confirmation prompt (auto-confirm)
38
+ -c, --config PATH Specify custom config file path
22
39
  -q, --quiet Hide all non-essential output (only errors or final status)
23
40
  --no-legend Hide all legends for config, mappings, and differences
24
41
  --no-config Hide the config section in the output
@@ -29,6 +46,7 @@ opt_parser = OptionParser.new do |opts|
29
46
  --only-config Show only the config section
30
47
  --only-mappings Show only the mappings section
31
48
  -v, --verbose Force showing all available information
49
+ --version Show version number
32
50
  -h, --help Show this help message
33
51
  BANNER
34
52
 
@@ -36,6 +54,18 @@ opt_parser = OptionParser.new do |opts|
36
54
  options[:apply] = true
37
55
  end
38
56
 
57
+ opts.on("--dry-run", "Preview changes without applying (default behavior)") do
58
+ options[:apply] = false
59
+ end
60
+
61
+ opts.on("-y", "--yes", "Skip confirmation prompt (auto-confirm)") do
62
+ options[:yes] = true
63
+ end
64
+
65
+ opts.on("-c", "--config PATH", "Specify custom config file path") do |path|
66
+ options[:config_path] = path
67
+ end
68
+
39
69
  opts.on("-q", "--quiet", "Hide all non-essential output (only errors or final status)") do
40
70
  options[:quiet] = true
41
71
  end
@@ -76,6 +106,11 @@ opt_parser = OptionParser.new do |opts|
76
106
  options[:verbose] = true
77
107
  end
78
108
 
109
+ opts.on("--version", "Show version number") do
110
+ puts "dotsync #{Dotsync::VERSION}"
111
+ exit
112
+ end
113
+
79
114
  opts.on("-h", "--help", "Show this help message") do
80
115
  puts opt_parser.banner
81
116
  exit
@@ -88,13 +123,27 @@ command = ARGV.shift
88
123
 
89
124
  case command
90
125
  when "push"
91
- Dotsync::Runner.new.run(:push, options)
126
+ Dotsync::Runner.new(config_path: options[:config_path]).run(:push, options)
92
127
  when "pull"
93
- Dotsync::Runner.new.run(:pull, options)
128
+ Dotsync::Runner.new(config_path: options[:config_path]).run(:pull, options)
94
129
  when "watch"
95
- Dotsync::Runner.new.run(:watch)
96
- when "setup"
97
- Dotsync::Runner.new.run(:setup)
130
+ Dotsync::Runner.new(config_path: options[:config_path]).run(:watch, options)
131
+ when "setup", "init"
132
+ Dotsync::Runner.new(config_path: options[:config_path]).run(:setup)
133
+ when "status"
134
+ # Show config and mappings without executing any action
135
+ # Use push action with only-config and only-mappings to display info
136
+ status_options = options.merge(
137
+ only_config: true,
138
+ only_mappings: true,
139
+ no_diff: true,
140
+ apply: false
141
+ )
142
+ Dotsync::Runner.new(config_path: options[:config_path]).run(:push, status_options)
143
+ when "diff"
144
+ # Alias for push in preview mode (default behavior)
145
+ diff_options = options.merge(apply: false)
146
+ Dotsync::Runner.new(config_path: options[:config_path]).run(:push, diff_options)
98
147
  else
99
148
  if command
100
149
  puts "dotsync: no such command '#{command}'"
@@ -80,6 +80,20 @@ module Dotsync
80
80
  def transfer_mappings
81
81
  valid_mappings.each do |mapping|
82
82
  Dotsync::FileTransfer.new(mapping).transfer
83
+ rescue Dotsync::PermissionError => e
84
+ logger.error("Permission denied: #{e.message}")
85
+ logger.info("Try: chmod +w <path> or check file permissions")
86
+ rescue Dotsync::DiskFullError => e
87
+ logger.error("Disk full: #{e.message}")
88
+ logger.info("Free up disk space and try again")
89
+ rescue Dotsync::SymlinkError => e
90
+ logger.error("Symlink error: #{e.message}")
91
+ logger.info("Check that symlink target exists and is accessible")
92
+ rescue Dotsync::TypeConflictError => e
93
+ logger.error("Type conflict: #{e.message}")
94
+ logger.info("Cannot overwrite directory with file or vice versa")
95
+ rescue Dotsync::FileTransferError => e
96
+ logger.error("File transfer failed: #{e.message}")
83
97
  end
84
98
  end
85
99
 
@@ -94,6 +108,15 @@ module Dotsync
94
108
  differs.any? { |differ| differ.any? }
95
109
  end
96
110
 
111
+ def confirm_action
112
+ total_changes = differs.sum { |diff| diff.additions.size + diff.modifications.size + diff.removals.size }
113
+ logger.log("")
114
+ logger.info("About to modify #{total_changes} file(s).", icon: :warning)
115
+ print "Continue? [y/N] "
116
+ response = $stdin.gets
117
+ response && response.strip.downcase == "y"
118
+ end
119
+
97
120
  def mappings_env_vars
98
121
  paths = mappings.flat_map do |mapping|
99
122
  [mapping.original_src, mapping.original_dest]
@@ -11,7 +11,7 @@ module Dotsync
11
11
  env_vars: !(quiet || options[:only_diff] || options[:only_mappings]),
12
12
  mappings_legend: !(quiet || options[:no_legend] || options[:no_mappings] || options[:only_diff]),
13
13
  mappings: !(quiet || options[:no_mappings] || options[:only_diff]),
14
- differences_legend: !(quiet || options[:no_legend] || options[:no_diff_legend] || options[:no_diff] || options[:only_config]),
14
+ differences_legend: !(quiet || options[:no_legend] || options[:no_diff_legend] || options[:no_diff] || options[:only_config] || options[:only_mappings]),
15
15
  differences: !(quiet || options[:no_diff] || options[:only_mappings] || options[:only_config])
16
16
  }
17
17
 
@@ -19,6 +19,11 @@ module Dotsync
19
19
 
20
20
  return unless options[:apply]
21
21
 
22
+ # Confirmation prompt unless --yes flag is provided or no differences
23
+ if has_differences? && !options[:yes] && !options[:quiet]
24
+ return unless confirm_action
25
+ end
26
+
22
27
  if has_differences?
23
28
  if create_backup
24
29
  show_backup
@@ -17,6 +17,11 @@ module Dotsync
17
17
 
18
18
  return unless options[:apply]
19
19
 
20
+ # Confirmation prompt unless --yes flag is provided or no differences
21
+ if has_differences? && !options[:yes] && !options[:quiet]
22
+ return unless confirm_action
23
+ end
24
+
20
25
  transfer_mappings
21
26
  action("Mappings pushed", icon: :done)
22
27
  end
@@ -12,14 +12,14 @@ module Dotsync
12
12
  setup_signal_trap
13
13
  end
14
14
 
15
- def execute
16
- show_mappings_legend
17
- show_mappings
15
+ def execute(options = {})
16
+ show_mappings_legend unless options[:quiet] || options[:no_legend] || options[:no_mappings]
17
+ show_mappings unless options[:quiet] || options[:no_mappings]
18
18
 
19
19
  @listeners.each(&:start)
20
20
 
21
- logger.action("Listening for changes...")
22
- logger.action("Press Ctrl+C to exit.")
21
+ logger.action("Listening for changes...") unless options[:quiet]
22
+ logger.action("Press Ctrl+C to exit.") unless options[:quiet]
23
23
  sleep
24
24
  end
25
25
 
@@ -9,6 +9,7 @@ module Dotsync
9
9
  @custom_colors = {}
10
10
 
11
11
  def self.load_custom_colors(config)
12
+ config ||= {}
12
13
  @custom_colors = {
13
14
  diff_additions: config.dig("colors", "diff_additions") || DEFAULT_DIFF_ADDITIONS,
14
15
  diff_modifications: config.dig("colors", "diff_modifications") || DEFAULT_DIFF_MODIFICATIONS,
@@ -17,15 +18,15 @@ module Dotsync
17
18
  end
18
19
 
19
20
  def self.diff_additions
20
- @custom_colors[:additions] || DEFAULT_DIFF_ADDITIONS
21
+ @custom_colors[:diff_additions] || DEFAULT_DIFF_ADDITIONS
21
22
  end
22
23
 
23
24
  def self.diff_modifications
24
- @custom_colors[:modifications] || DEFAULT_DIFF_MODIFICATIONS
25
+ @custom_colors[:diff_modifications] || DEFAULT_DIFF_MODIFICATIONS
25
26
  end
26
27
 
27
28
  def self.diff_removals
28
- @custom_colors[:removals] || DEFAULT_DIFF_REMOVALS
29
+ @custom_colors[:diff_removals] || DEFAULT_DIFF_REMOVALS
29
30
  end
30
31
 
31
32
  MAPPINGS = {
@@ -12,6 +12,14 @@ module Dotsync
12
12
  # @param [String] path The file path to the configuration file.
13
13
  def initialize(path = Dotsync.config_path)
14
14
  absolute_path = File.expand_path(path)
15
+
16
+ unless File.exist?(absolute_path)
17
+ raise Dotsync::ConfigError,
18
+ "Config file not found: #{absolute_path}\n\n" \
19
+ "To create a default configuration file, run:\n" \
20
+ " dotsync setup"
21
+ end
22
+
15
23
  @config = TomlRB.load_file(absolute_path)
16
24
  validate!
17
25
  end
@@ -4,19 +4,19 @@ module Dotsync
4
4
  # https://specifications.freedesktop.org/basedir-spec/latest/
5
5
  module XDGBaseDirectory
6
6
  def xdg_data_home
7
- File.expand_path(ENV["XDG_DATA_HOME"] || "$HOME/.local/share")
7
+ File.expand_path(ENV["XDG_DATA_HOME"] || "~/.local/share")
8
8
  end
9
9
 
10
10
  def xdg_config_home
11
- File.expand_path(ENV["XDG_CONFIG_HOME"] || "$HOME/.config")
11
+ File.expand_path(ENV["XDG_CONFIG_HOME"] || "~/.config")
12
12
  end
13
13
 
14
14
  def xdg_cache_home
15
- File.expand_path(ENV["XDG_CACHE_HOME"] || "$HOME/.cache")
15
+ File.expand_path(ENV["XDG_CACHE_HOME"] || "~/.cache")
16
16
  end
17
17
 
18
18
  def xdg_bin_home
19
- File.expand_path(ENV["XDG_BIN_HOME"] || "$HOME/.local/bin")
19
+ File.expand_path(ENV["XDG_BIN_HOME"] || "~/.local/bin")
20
20
  end
21
21
  end
22
22
  end
@@ -2,8 +2,9 @@
2
2
 
3
3
  module Dotsync
4
4
  class Runner
5
- def initialize(logger: nil)
5
+ def initialize(logger: nil, config_path: nil)
6
6
  @logger = logger || Dotsync::Logger.new
7
+ @config_path = config_path
7
8
  end
8
9
 
9
10
  # action_name should be a symbol, e.g., :pull, :watch, :sync
@@ -44,7 +45,7 @@ module Dotsync
44
45
  require "toml-rb"
45
46
  require "fileutils"
46
47
 
47
- config_path = File.expand_path(Dotsync.config_path)
48
+ config_path = File.expand_path(@config_path || Dotsync.config_path)
48
49
  FileUtils.mkdir_p(File.dirname(config_path))
49
50
 
50
51
  example_mappings = {
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dotsync
4
- VERSION = "0.1.20"
4
+ VERSION = "0.1.22"
5
5
  end
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.1.20
4
+ version: 0.1.22
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Sáenz
@@ -248,6 +248,34 @@ dependencies:
248
248
  - - "~>"
249
249
  - !ruby/object:Gem::Version
250
250
  version: 0.26.1
251
+ - !ruby/object:Gem::Dependency
252
+ name: simplecov
253
+ requirement: !ruby/object:Gem::Requirement
254
+ requirements:
255
+ - - "~>"
256
+ - !ruby/object:Gem::Version
257
+ version: 0.22.0
258
+ type: :development
259
+ prerelease: false
260
+ version_requirements: !ruby/object:Gem::Requirement
261
+ requirements:
262
+ - - "~>"
263
+ - !ruby/object:Gem::Version
264
+ version: 0.22.0
265
+ - !ruby/object:Gem::Dependency
266
+ name: bundler-audit
267
+ requirement: !ruby/object:Gem::Requirement
268
+ requirements:
269
+ - - "~>"
270
+ - !ruby/object:Gem::Version
271
+ version: 0.9.0
272
+ type: :development
273
+ prerelease: false
274
+ version_requirements: !ruby/object:Gem::Requirement
275
+ requirements:
276
+ - - "~>"
277
+ - !ruby/object:Gem::Version
278
+ version: 0.9.0
251
279
  description: Keep in sync your dotfiles across machines with a single TOML file
252
280
  email:
253
281
  - david.saenz.tagarro@gmail.com
@@ -256,6 +284,7 @@ executables:
256
284
  extensions: []
257
285
  extra_rdoc_files: []
258
286
  files:
287
+ - ".editorconfig"
259
288
  - ".github/workflows/gem-push.yml"
260
289
  - ".github/workflows/gem-push.yml.bak"
261
290
  - ".gitignore"