dotman-git 1.0.0__py3-none-any.whl
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.
- dot_man/__init__.py +4 -0
- dot_man/backups.py +211 -0
- dot_man/branch_ops.py +347 -0
- dot_man/cli/__init__.py +113 -0
- dot_man/cli/add_cmd.py +167 -0
- dot_man/cli/audit_cmd.py +141 -0
- dot_man/cli/backup_cmd.py +105 -0
- dot_man/cli/branch_cmd.py +103 -0
- dot_man/cli/clean_cmd.py +97 -0
- dot_man/cli/common.py +548 -0
- dot_man/cli/completions_cmd.py +127 -0
- dot_man/cli/config_cmd.py +979 -0
- dot_man/cli/deploy_cmd.py +169 -0
- dot_man/cli/discover_cmd.py +105 -0
- dot_man/cli/doctor_cmd.py +229 -0
- dot_man/cli/edit_cmd.py +177 -0
- dot_man/cli/encrypt_cmd.py +205 -0
- dot_man/cli/export_cmd.py +146 -0
- dot_man/cli/import_cmd.py +315 -0
- dot_man/cli/init_cmd.py +532 -0
- dot_man/cli/interface.py +56 -0
- dot_man/cli/log_cmd.py +339 -0
- dot_man/cli/main.py +36 -0
- dot_man/cli/navigate_cmd.py +903 -0
- dot_man/cli/onboarding.py +546 -0
- dot_man/cli/profile_cmd.py +313 -0
- dot_man/cli/remote_cmd.py +454 -0
- dot_man/cli/restore_cmd.py +82 -0
- dot_man/cli/revert_cmd.py +86 -0
- dot_man/cli/show_cmd.py +29 -0
- dot_man/cli/status_cmd.py +185 -0
- dot_man/cli/switch_cmd.py +387 -0
- dot_man/cli/tag_cmd.py +164 -0
- dot_man/cli/template_cmd.py +244 -0
- dot_man/cli/tui_cmd.py +44 -0
- dot_man/cli/verify_cmd.py +156 -0
- dot_man/completions/_dot-man.zsh +28 -0
- dot_man/completions/dot-man.bash +15 -0
- dot_man/completions/dot-man.fish +58 -0
- dot_man/completions/install.sh +26 -0
- dot_man/config.py +23 -0
- dot_man/config_detector.py +426 -0
- dot_man/constants.py +109 -0
- dot_man/core.py +614 -0
- dot_man/dotman_config.py +516 -0
- dot_man/encryption.py +173 -0
- dot_man/exceptions.py +255 -0
- dot_man/files.py +443 -0
- dot_man/global_config.py +305 -0
- dot_man/hooks.py +232 -0
- dot_man/interactive.py +460 -0
- dot_man/lock.py +64 -0
- dot_man/merge.py +440 -0
- dot_man/operations.py +212 -0
- dot_man/py.typed +1 -0
- dot_man/save_deploy_ops.py +466 -0
- dot_man/secrets.py +473 -0
- dot_man/section.py +207 -0
- dot_man/status_ops.py +229 -0
- dot_man/tui_log.py +91 -0
- dot_man/ui.py +127 -0
- dot_man/utils.py +132 -0
- dot_man/vault.py +317 -0
- dotman_git-1.0.0.dist-info/METADATA +678 -0
- dotman_git-1.0.0.dist-info/RECORD +69 -0
- dotman_git-1.0.0.dist-info/WHEEL +5 -0
- dotman_git-1.0.0.dist-info/entry_points.txt +3 -0
- dotman_git-1.0.0.dist-info/licenses/LICENSE +21 -0
- dotman_git-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,979 @@
|
|
|
1
|
+
"""Config command for dot-man CLI."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
from rich.table import Table
|
|
7
|
+
|
|
8
|
+
from .. import ui
|
|
9
|
+
from ..config import GlobalConfig
|
|
10
|
+
from ..exceptions import ConfigurationError
|
|
11
|
+
from .common import complete_config_keys, error, require_init, success
|
|
12
|
+
from .interface import cli as main
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@main.group()
|
|
16
|
+
def config():
|
|
17
|
+
"""Manage global configuration."""
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@config.command("defaults")
|
|
22
|
+
def config_defaults():
|
|
23
|
+
"""Show all configurable defaults with descriptions.
|
|
24
|
+
|
|
25
|
+
This displays all the settings that users can customize,
|
|
26
|
+
along with their current default values.
|
|
27
|
+
|
|
28
|
+
Example: dot-man config defaults
|
|
29
|
+
"""
|
|
30
|
+
from rich.table import Table
|
|
31
|
+
|
|
32
|
+
table = Table(title="Configurable Defaults", show_header=True)
|
|
33
|
+
table.add_column("Setting", style="cyan", no_wrap=True)
|
|
34
|
+
table.add_column("Default Value", style="green")
|
|
35
|
+
table.add_column("Description")
|
|
36
|
+
|
|
37
|
+
defaults = [
|
|
38
|
+
# Switch/Navigate settings
|
|
39
|
+
(
|
|
40
|
+
"switch.default_behavior",
|
|
41
|
+
"save",
|
|
42
|
+
"What to do with unsaved changes when switching (save/no-save)",
|
|
43
|
+
),
|
|
44
|
+
(
|
|
45
|
+
"remote.auto_sync",
|
|
46
|
+
"false",
|
|
47
|
+
"Auto push/pull when switching branches (true/false)",
|
|
48
|
+
),
|
|
49
|
+
("remote.url", "", "Remote repository URL for sync"),
|
|
50
|
+
# Default section settings
|
|
51
|
+
("defaults.secrets_filter", "true", "Redact secrets when saving (true/false)"),
|
|
52
|
+
(
|
|
53
|
+
"defaults.update_strategy",
|
|
54
|
+
"replace",
|
|
55
|
+
"How to deploy: replace/rename_old/ignore",
|
|
56
|
+
),
|
|
57
|
+
(
|
|
58
|
+
"defaults.follow_symlinks",
|
|
59
|
+
"false",
|
|
60
|
+
"Follow symlinks when deploying (true/false)",
|
|
61
|
+
),
|
|
62
|
+
# Security
|
|
63
|
+
(
|
|
64
|
+
"security.strict_mode",
|
|
65
|
+
"false",
|
|
66
|
+
"Exit with error if secrets detected (true/false)",
|
|
67
|
+
),
|
|
68
|
+
("security.audit_on_commit", "true", "Run audit before commits (true/false)"),
|
|
69
|
+
# Other
|
|
70
|
+
("backup.max_count", "5", "Maximum number of backups to keep"),
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
for key, default_val, desc in defaults:
|
|
74
|
+
table.add_row(key, str(default_val), desc)
|
|
75
|
+
|
|
76
|
+
ui.console.print(table)
|
|
77
|
+
ui.console.print()
|
|
78
|
+
ui.console.print("[bold]Section-level settings (in dot-man.toml):[/bold]")
|
|
79
|
+
ui.console.print(" [cyan]paths[/cyan] - List of files/dirs to track")
|
|
80
|
+
ui.console.print(" [cyan]secrets_filter[/cyan] - Enable secret detection")
|
|
81
|
+
ui.console.print(" [cyan]update_strategy[/cyan] - How to handle existing files")
|
|
82
|
+
ui.console.print(" [cyan]pre_deploy[/cyan] - Command to run before deploying")
|
|
83
|
+
ui.console.print(" [cyan]post_deploy[/cyan] - Command to run after deploying")
|
|
84
|
+
ui.console.print()
|
|
85
|
+
ui.console.print("[bold]To change a setting:[/bold]")
|
|
86
|
+
ui.console.print(" [cyan]dot-man config set <key> <value>[/cyan]")
|
|
87
|
+
ui.console.print()
|
|
88
|
+
ui.console.print("[dim]Examples:[/dim]")
|
|
89
|
+
ui.console.print(" dot-man config set switch.default_behavior no-save")
|
|
90
|
+
ui.console.print(" dot-man config set remote.auto_sync true")
|
|
91
|
+
ui.console.print(" dot-man config set defaults.update_strategy rename_old")
|
|
92
|
+
ui.console.print()
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@config.command("list")
|
|
96
|
+
def config_list():
|
|
97
|
+
"""List all global configuration values."""
|
|
98
|
+
try:
|
|
99
|
+
cfg = GlobalConfig()
|
|
100
|
+
cfg.load()
|
|
101
|
+
|
|
102
|
+
def flatten(d, parent_key="", sep="."):
|
|
103
|
+
items = []
|
|
104
|
+
for k, v in d.items():
|
|
105
|
+
new_key = f"{parent_key}{sep}{k}" if parent_key else k
|
|
106
|
+
if isinstance(v, dict):
|
|
107
|
+
items.extend(flatten(v, new_key, sep=sep).items())
|
|
108
|
+
else:
|
|
109
|
+
items.append((new_key, v))
|
|
110
|
+
return dict(items)
|
|
111
|
+
|
|
112
|
+
flat_data = flatten(cfg._data)
|
|
113
|
+
|
|
114
|
+
table = Table(title="Global Configuration")
|
|
115
|
+
table.add_column("Key", style="cyan")
|
|
116
|
+
table.add_column("Value")
|
|
117
|
+
|
|
118
|
+
for k, v in sorted(flat_data.items()):
|
|
119
|
+
table.add_row(k, str(v))
|
|
120
|
+
|
|
121
|
+
ui.console.print(table)
|
|
122
|
+
|
|
123
|
+
except Exception as e:
|
|
124
|
+
error(f"Failed to list config: {e}")
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@config.command("get")
|
|
128
|
+
@click.argument("key", shell_complete=complete_config_keys)
|
|
129
|
+
def config_get(key: str):
|
|
130
|
+
"""Get a configuration value.
|
|
131
|
+
|
|
132
|
+
Example: dot-man config get dot-man.editor
|
|
133
|
+
"""
|
|
134
|
+
try:
|
|
135
|
+
cfg = GlobalConfig()
|
|
136
|
+
cfg.load()
|
|
137
|
+
|
|
138
|
+
parts = key.split(".")
|
|
139
|
+
current = cfg._data
|
|
140
|
+
|
|
141
|
+
for part in parts:
|
|
142
|
+
if isinstance(current, dict) and part in current:
|
|
143
|
+
current = current[part]
|
|
144
|
+
else:
|
|
145
|
+
ui.console.print(f"[red]Key not found:[/red] {key}")
|
|
146
|
+
ui.hint("Run 'dot-man config list' to see all available keys")
|
|
147
|
+
raise SystemExit(1)
|
|
148
|
+
|
|
149
|
+
if isinstance(current, dict):
|
|
150
|
+
ui.console.print(f"[dim]Section '{key}' contains:[/dim]")
|
|
151
|
+
ui.console.print(json.dumps(current, indent=2))
|
|
152
|
+
else:
|
|
153
|
+
ui.console.print(str(current))
|
|
154
|
+
|
|
155
|
+
except Exception as e:
|
|
156
|
+
error(f"Failed to get config: {e}")
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@config.command("set")
|
|
160
|
+
@click.argument("key", shell_complete=complete_config_keys)
|
|
161
|
+
@click.argument("value")
|
|
162
|
+
def config_set(key: str, value: str):
|
|
163
|
+
"""Set a configuration value.
|
|
164
|
+
|
|
165
|
+
Example: dot-man config set dot-man.editor nvim
|
|
166
|
+
"""
|
|
167
|
+
try:
|
|
168
|
+
cfg = GlobalConfig()
|
|
169
|
+
try:
|
|
170
|
+
cfg.load()
|
|
171
|
+
except (FileNotFoundError, ConfigurationError):
|
|
172
|
+
cfg.create_default()
|
|
173
|
+
|
|
174
|
+
val: bool | str
|
|
175
|
+
if value.lower() == "true":
|
|
176
|
+
val = True
|
|
177
|
+
elif value.lower() == "false":
|
|
178
|
+
val = False
|
|
179
|
+
else:
|
|
180
|
+
val = value
|
|
181
|
+
|
|
182
|
+
parts = key.split(".")
|
|
183
|
+
current = cfg._data
|
|
184
|
+
|
|
185
|
+
for i, part in enumerate(parts[:-1]):
|
|
186
|
+
if part not in current:
|
|
187
|
+
current[part] = {}
|
|
188
|
+
current = current[part]
|
|
189
|
+
if not isinstance(current, dict):
|
|
190
|
+
error(
|
|
191
|
+
f"Key path conflict: '{'.'.join(parts[: i + 1])}' is not a section"
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
current[parts[-1]] = val
|
|
195
|
+
cfg.save()
|
|
196
|
+
|
|
197
|
+
success(f"Set '{key}' to '{val}'")
|
|
198
|
+
ui.console.print(f"[dim] Verified: {key} = {val}[/dim]")
|
|
199
|
+
|
|
200
|
+
except Exception as e:
|
|
201
|
+
error(f"Failed to set config: {e}")
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
@config.command("create")
|
|
205
|
+
@click.option(
|
|
206
|
+
"--examples",
|
|
207
|
+
"with_examples",
|
|
208
|
+
is_flag=True,
|
|
209
|
+
default=True,
|
|
210
|
+
help="Include commented examples in the config file (default: True)",
|
|
211
|
+
)
|
|
212
|
+
@click.option(
|
|
213
|
+
"--minimal",
|
|
214
|
+
is_flag=True,
|
|
215
|
+
help="Create a minimal config file without examples or comments",
|
|
216
|
+
)
|
|
217
|
+
@click.option(
|
|
218
|
+
"--force", is_flag=True, help="Overwrite existing config file without prompting"
|
|
219
|
+
)
|
|
220
|
+
@require_init
|
|
221
|
+
def config_create(with_examples: bool, minimal: bool, force: bool):
|
|
222
|
+
"""Create or regenerate the dot-man.toml configuration file.
|
|
223
|
+
|
|
224
|
+
This command creates a new dot-man.toml file for the current branch.
|
|
225
|
+
By default, it includes commented examples to help you get started.
|
|
226
|
+
|
|
227
|
+
Examples:
|
|
228
|
+
# Create config with examples (default)
|
|
229
|
+
dot-man config create
|
|
230
|
+
|
|
231
|
+
# Create minimal config without examples
|
|
232
|
+
dot-man config create --minimal
|
|
233
|
+
|
|
234
|
+
# Create config with examples, overwrite existing
|
|
235
|
+
dot-man config create --examples --force
|
|
236
|
+
|
|
237
|
+
# Create minimal config, overwrite existing
|
|
238
|
+
dot-man config create --minimal --force
|
|
239
|
+
"""
|
|
240
|
+
try:
|
|
241
|
+
from ..config import DotManConfig
|
|
242
|
+
from ..constants import DOT_MAN_TOML, REPO_DIR
|
|
243
|
+
|
|
244
|
+
config_path = REPO_DIR / DOT_MAN_TOML
|
|
245
|
+
|
|
246
|
+
# Check if file exists
|
|
247
|
+
if config_path.exists() and not force:
|
|
248
|
+
if not ui.confirm(
|
|
249
|
+
f"Config file already exists at {config_path}. Overwrite?"
|
|
250
|
+
):
|
|
251
|
+
ui.console.print("Cancelled.")
|
|
252
|
+
return
|
|
253
|
+
|
|
254
|
+
# Create the config
|
|
255
|
+
dotman_config = DotManConfig()
|
|
256
|
+
|
|
257
|
+
if minimal:
|
|
258
|
+
# Create minimal config without examples
|
|
259
|
+
dotman_config._data = {}
|
|
260
|
+
dotman_config.save()
|
|
261
|
+
ui.console.print(f"Created minimal config at {config_path}")
|
|
262
|
+
else:
|
|
263
|
+
# Create config with examples (default behavior)
|
|
264
|
+
dotman_config.create_default()
|
|
265
|
+
ui.console.print(f"Created config with examples at {config_path}")
|
|
266
|
+
|
|
267
|
+
ui.console.print("Tip: Use 'dot-man edit' to open the config in your editor")
|
|
268
|
+
|
|
269
|
+
except Exception as e:
|
|
270
|
+
error(f"Failed to create config: {e}")
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
@config.command("tutorial")
|
|
274
|
+
@click.option("--section", help="Show examples for a specific section type")
|
|
275
|
+
@click.option("--interactive", "-i", is_flag=True, help="Interactive tutorial mode")
|
|
276
|
+
def config_tutorial(section: str | None, interactive: bool):
|
|
277
|
+
"""Interactive configuration tutorial with examples.
|
|
278
|
+
|
|
279
|
+
Learn how to configure dot-man with practical examples and explanations.
|
|
280
|
+
Similar to vimtutor, this guides you through configuration options.
|
|
281
|
+
|
|
282
|
+
Examples:
|
|
283
|
+
# Show all examples
|
|
284
|
+
dot-man config tutorial
|
|
285
|
+
|
|
286
|
+
# Show examples for a specific type
|
|
287
|
+
dot-man config tutorial --section basic
|
|
288
|
+
dot-man config tutorial --section advanced
|
|
289
|
+
dot-man config tutorial --section hooks
|
|
290
|
+
|
|
291
|
+
# Interactive mode (step by step)
|
|
292
|
+
dot-man config tutorial --interactive
|
|
293
|
+
"""
|
|
294
|
+
from rich.panel import Panel
|
|
295
|
+
from rich.prompt import Prompt
|
|
296
|
+
|
|
297
|
+
if interactive:
|
|
298
|
+
_run_interactive_tutorial()
|
|
299
|
+
return
|
|
300
|
+
|
|
301
|
+
if section:
|
|
302
|
+
_show_section_examples(section)
|
|
303
|
+
return
|
|
304
|
+
|
|
305
|
+
# Show interactive overview with all sections
|
|
306
|
+
ui.console.print()
|
|
307
|
+
ui.console.print(
|
|
308
|
+
Panel.fit(
|
|
309
|
+
"[bold blue]dot-man Configuration Tutorial[/bold blue]\n\n"
|
|
310
|
+
"This tutorial shows you how to configure dot-man to track your dotfiles.\n"
|
|
311
|
+
"Choose from the options below or use --interactive for guided learning.",
|
|
312
|
+
title="🎯 Tutorial Overview",
|
|
313
|
+
)
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
ui.console.print("\n[bold]What would you like to learn about?[/bold]")
|
|
317
|
+
ui.console.print()
|
|
318
|
+
|
|
319
|
+
# Interactive menu options
|
|
320
|
+
menu_options = [
|
|
321
|
+
("1", "Basic file tracking", "paths, sections, simple examples"),
|
|
322
|
+
("2", "Directory tracking", "include/exclude patterns, wildcards"),
|
|
323
|
+
("3", "Update strategies", "replace, rename_old, ignore strategies"),
|
|
324
|
+
("4", "Hooks & automation", "pre/post deploy commands, aliases"),
|
|
325
|
+
("5", "Templates & inheritance", "reusable configs, organization"),
|
|
326
|
+
("6", "Advanced features", "custom paths, overrides, limits"),
|
|
327
|
+
("7", "Security & secrets", "automatic filtering, best practices"),
|
|
328
|
+
("8", "Branch activation", "on_activate, on_deactivate hooks"),
|
|
329
|
+
("9", "Quick presets", "pre-configured for popular dotfiles"),
|
|
330
|
+
("I", "Interactive tutorial", "step-by-step guided learning"),
|
|
331
|
+
("C", "Create config", "generate config file with examples"),
|
|
332
|
+
("Q", "Quit", "exit tutorial"),
|
|
333
|
+
]
|
|
334
|
+
|
|
335
|
+
for key, title, desc in menu_options:
|
|
336
|
+
if key in ["I", "C", "Q"]:
|
|
337
|
+
ui.console.print(
|
|
338
|
+
f" [yellow]{key}[/yellow] - [bold]{title}[/bold] - {desc}"
|
|
339
|
+
)
|
|
340
|
+
else:
|
|
341
|
+
ui.console.print(f" [cyan]{key}[/cyan] - [bold]{title}[/bold] - {desc}")
|
|
342
|
+
|
|
343
|
+
ui.console.print()
|
|
344
|
+
|
|
345
|
+
# Get user choice
|
|
346
|
+
choice = Prompt.ask(
|
|
347
|
+
"Enter your choice",
|
|
348
|
+
choices=["1", "2", "3", "4", "5", "6", "7", "8", "9", "I", "C", "Q"],
|
|
349
|
+
default="I",
|
|
350
|
+
).upper()
|
|
351
|
+
|
|
352
|
+
# Handle choice
|
|
353
|
+
if choice == "1":
|
|
354
|
+
_show_section_examples("basic")
|
|
355
|
+
elif choice == "2":
|
|
356
|
+
_show_section_examples("directories")
|
|
357
|
+
elif choice == "3":
|
|
358
|
+
_show_section_examples("hooks") # Update strategies are in hooks section
|
|
359
|
+
elif choice == "4":
|
|
360
|
+
_show_section_examples("hooks")
|
|
361
|
+
elif choice == "5":
|
|
362
|
+
_show_section_examples("templates")
|
|
363
|
+
elif choice == "6":
|
|
364
|
+
_show_section_examples("advanced")
|
|
365
|
+
elif choice == "7":
|
|
366
|
+
_show_section_examples("secrets")
|
|
367
|
+
elif choice == "8":
|
|
368
|
+
_show_section_examples("activate")
|
|
369
|
+
elif choice == "9":
|
|
370
|
+
_show_section_examples("presets")
|
|
371
|
+
elif choice == "I":
|
|
372
|
+
_run_interactive_tutorial()
|
|
373
|
+
elif choice == "C":
|
|
374
|
+
ui.console.print(
|
|
375
|
+
"\n[dim]Tip: Run 'dot-man config create' to generate a config file with examples[/dim]"
|
|
376
|
+
)
|
|
377
|
+
elif choice == "Q":
|
|
378
|
+
ui.console.print(
|
|
379
|
+
"\n[dim]Goodbye! Run 'dot-man config tutorial' anytime to return.[/dim]"
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
return
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def _show_section_examples(section: str):
|
|
386
|
+
"""Show examples for a specific section."""
|
|
387
|
+
from typing import Any
|
|
388
|
+
|
|
389
|
+
from rich.panel import Panel
|
|
390
|
+
from rich.syntax import Syntax
|
|
391
|
+
|
|
392
|
+
examples: dict[str, dict[str, Any]] = {
|
|
393
|
+
"basic": {
|
|
394
|
+
"title": "Basic File Tracking",
|
|
395
|
+
"description": "Track individual files with smart defaults",
|
|
396
|
+
"examples": [
|
|
397
|
+
{
|
|
398
|
+
"title": "Simple file tracking",
|
|
399
|
+
"config": """[bashrc]
|
|
400
|
+
paths = ["~/.bashrc"]""",
|
|
401
|
+
"explanation": "Tracks your bash configuration. dot-man automatically:\n"
|
|
402
|
+
"• Generates repo_base as 'bashrc'\n"
|
|
403
|
+
"• Uses 'replace' update_strategy\n"
|
|
404
|
+
"• Enables secrets_filter",
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
"title": "Multiple files in one section",
|
|
408
|
+
"config": """[shell-files]
|
|
409
|
+
paths = ["~/.bashrc", "~/.zshrc", "~/.profile"]""",
|
|
410
|
+
"explanation": "Group related files together. All files share the same settings.",
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
"title": "Custom repository name",
|
|
414
|
+
"config": """[my-config]
|
|
415
|
+
paths = ["~/.myapp/config"]
|
|
416
|
+
repo_base = "my-app-config" """,
|
|
417
|
+
"explanation": "Override the auto-generated repo_base with a custom name.",
|
|
418
|
+
},
|
|
419
|
+
],
|
|
420
|
+
},
|
|
421
|
+
"directories": {
|
|
422
|
+
"title": "Directory Tracking",
|
|
423
|
+
"description": "Track entire directories with include/exclude patterns",
|
|
424
|
+
"examples": [
|
|
425
|
+
{
|
|
426
|
+
"title": "Basic directory tracking",
|
|
427
|
+
"config": """[nvim]
|
|
428
|
+
paths = ["~/.config/nvim"]""",
|
|
429
|
+
"explanation": "Tracks your entire Neovim config directory.",
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
"title": "Directory with exclusions",
|
|
433
|
+
"config": """[nvim]
|
|
434
|
+
paths = ["~/.config/nvim"]
|
|
435
|
+
exclude = ["*.log", "plugin/packer_compiled.lua"]""",
|
|
436
|
+
"explanation": "Exclude temporary files and compiled plugins from tracking.",
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
"title": "Include only specific files",
|
|
440
|
+
"config": """[dotfiles]
|
|
441
|
+
paths = ["~/dotfiles"]
|
|
442
|
+
include = ["*.conf", "*.sh", "README.md"]""",
|
|
443
|
+
"explanation": "Only track configuration files, scripts, and documentation.",
|
|
444
|
+
},
|
|
445
|
+
],
|
|
446
|
+
},
|
|
447
|
+
"hooks": {
|
|
448
|
+
"title": "Pre/Post Deploy Hooks",
|
|
449
|
+
"description": "Run commands before or after file deployment",
|
|
450
|
+
"examples": [
|
|
451
|
+
{
|
|
452
|
+
"title": "Shell reload after config change",
|
|
453
|
+
"config": """[bashrc]
|
|
454
|
+
paths = ["~/.bashrc"]
|
|
455
|
+
post_deploy = "shell_reload" """,
|
|
456
|
+
"explanation": "Reloads your shell after deploying bash config.\n"
|
|
457
|
+
"'shell_reload' is an alias for: source ~/.bashrc || source ~/.zshrc",
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
"title": "Neovim plugin sync",
|
|
461
|
+
"config": """[nvim]
|
|
462
|
+
paths = ["~/.config/nvim"]
|
|
463
|
+
post_deploy = "nvim_sync" """,
|
|
464
|
+
"explanation": "Runs PackerSync after deploying Neovim config.\n"
|
|
465
|
+
"'nvim_sync' is an alias for: nvim --headless +PackerSync +qa",
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
"title": "Custom command",
|
|
469
|
+
"config": """[custom-app]
|
|
470
|
+
paths = ["~/.config/myapp"]
|
|
471
|
+
post_deploy = "systemctl --user restart myapp" """,
|
|
472
|
+
"explanation": "Restart a user service after config deployment.",
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
"title": "Pre-deploy backup",
|
|
476
|
+
"config": """[important-config]
|
|
477
|
+
paths = ["~/.important"]
|
|
478
|
+
pre_deploy = "cp ~/.important ~/.important.backup" """,
|
|
479
|
+
"explanation": "Create a backup before overwriting important files.",
|
|
480
|
+
},
|
|
481
|
+
],
|
|
482
|
+
},
|
|
483
|
+
"templates": {
|
|
484
|
+
"title": "Reusable Templates",
|
|
485
|
+
"description": "Define shared settings that can be inherited",
|
|
486
|
+
"examples": [
|
|
487
|
+
{
|
|
488
|
+
"title": "Template definition",
|
|
489
|
+
"config": """[templates.linux-desktop]
|
|
490
|
+
post_deploy = "notify-send 'Config updated'"
|
|
491
|
+
update_strategy = "rename_old"
|
|
492
|
+
|
|
493
|
+
[templates.dev-tools]
|
|
494
|
+
secrets_filter = false
|
|
495
|
+
pre_deploy = "echo 'Deploying dev config'" """,
|
|
496
|
+
"explanation": "Define reusable templates with common settings.",
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
"title": "Template inheritance",
|
|
500
|
+
"config": """[hyprland]
|
|
501
|
+
paths = ["~/.config/hypr"]
|
|
502
|
+
inherits = ["linux-desktop"]
|
|
503
|
+
|
|
504
|
+
[git]
|
|
505
|
+
paths = ["~/.gitconfig"]
|
|
506
|
+
inherits = ["dev-tools"]""",
|
|
507
|
+
"explanation": "Inherit settings from templates. Child settings override parent settings.",
|
|
508
|
+
},
|
|
509
|
+
],
|
|
510
|
+
},
|
|
511
|
+
"advanced": {
|
|
512
|
+
"title": "Advanced Options",
|
|
513
|
+
"description": "Fine-tune behavior with advanced configuration options",
|
|
514
|
+
"examples": [
|
|
515
|
+
{
|
|
516
|
+
"title": "Update strategies",
|
|
517
|
+
"config": """[careful-config]
|
|
518
|
+
paths = ["~/.important"]
|
|
519
|
+
update_strategy = "rename_old"
|
|
520
|
+
|
|
521
|
+
[aggressive-config]
|
|
522
|
+
paths = ["~/.cache/myapp"]
|
|
523
|
+
update_strategy = "replace"
|
|
524
|
+
|
|
525
|
+
[readonly-config]
|
|
526
|
+
paths = ["~/.readonly"]
|
|
527
|
+
update_strategy = "ignore" """,
|
|
528
|
+
"explanation": "• 'replace': Overwrite existing files (default)\n"
|
|
529
|
+
"• 'rename_old': Backup existing files\n"
|
|
530
|
+
"• 'ignore': Skip if file exists",
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
"title": "Explicit repository paths",
|
|
534
|
+
"config": """[special-file]
|
|
535
|
+
paths = ["~/.config/app/special.conf"]
|
|
536
|
+
repo_path = "configs/special-config.toml" """,
|
|
537
|
+
"explanation": "Override automatic repo path generation with explicit repo_path.",
|
|
538
|
+
},
|
|
539
|
+
],
|
|
540
|
+
},
|
|
541
|
+
"secrets": {
|
|
542
|
+
"title": "Secret Detection & Filtering",
|
|
543
|
+
"description": "Automatically detect and handle sensitive information",
|
|
544
|
+
"examples": [
|
|
545
|
+
{
|
|
546
|
+
"title": "Automatic secret filtering",
|
|
547
|
+
"config": """[gitconfig]
|
|
548
|
+
paths = ["~/.gitconfig"]
|
|
549
|
+
# secrets_filter = true (enabled by default)""",
|
|
550
|
+
"explanation": "Automatically redacts API keys, passwords, and tokens when saving.",
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
"title": "Disable filtering for trusted files",
|
|
554
|
+
"config": """[trusted-config]
|
|
555
|
+
paths = ["~/.config/trusted"]
|
|
556
|
+
secrets_filter = false""",
|
|
557
|
+
"explanation": "Disable secret filtering for files you know are safe.",
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
"title": "Check for secrets",
|
|
561
|
+
"command": "dot-man audit",
|
|
562
|
+
"explanation": "Scan your repository for secrets. Use --strict for CI/CD.",
|
|
563
|
+
},
|
|
564
|
+
],
|
|
565
|
+
},
|
|
566
|
+
"activate": {
|
|
567
|
+
"title": "Branch Activation Hooks",
|
|
568
|
+
"description": "Run commands when entering/leaving branches",
|
|
569
|
+
"examples": [
|
|
570
|
+
{
|
|
571
|
+
"title": "Start app on branch switch",
|
|
572
|
+
"config": """[dots]
|
|
573
|
+
paths = [".config/quickshell"]
|
|
574
|
+
on_activate = "qs -c ii"
|
|
575
|
+
on_deactivate = "pkill qs -9" """,
|
|
576
|
+
"explanation": "Run 'qs -c ii' when switching TO this branch, "
|
|
577
|
+
"and 'pkill qs -9' when leaving. Perfect for launching "
|
|
578
|
+
"config-specific applications.",
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
"title": "Reload environment",
|
|
582
|
+
"config": """[work]
|
|
583
|
+
paths = [".config/work"]
|
|
584
|
+
on_activate = "source ~/.config/work/env.sh"
|
|
585
|
+
on_deactivate = "echo 'Leaving work config'" """,
|
|
586
|
+
"explanation": "Load environment variables or run setup commands "
|
|
587
|
+
"when entering a branch.",
|
|
588
|
+
},
|
|
589
|
+
{
|
|
590
|
+
"title": "Multiple hooks",
|
|
591
|
+
"config": """[dev]
|
|
592
|
+
paths = [".config/dev"]
|
|
593
|
+
on_activate = "echo 'Starting dev mode' && alacritty -e tmux"
|
|
594
|
+
on_deactivate = "pkill -f 'alacritty -e tmux'" """,
|
|
595
|
+
"explanation": "Chain multiple commands with && for complex activation.",
|
|
596
|
+
},
|
|
597
|
+
],
|
|
598
|
+
},
|
|
599
|
+
"presets": {
|
|
600
|
+
"title": "Quick Setup Presets",
|
|
601
|
+
"description": "Pre-configured sections for popular dotfiles",
|
|
602
|
+
"examples": [
|
|
603
|
+
{
|
|
604
|
+
"title": "Quickshell end-4",
|
|
605
|
+
"config": """[qs-end4]
|
|
606
|
+
paths = [".config/quickshell/end-4"]
|
|
607
|
+
on_activate = "qs -c end-4"
|
|
608
|
+
on_deactivate = "pkill qs -9" """,
|
|
609
|
+
"explanation": "Quickshell with config 'end-4'. Auto-detected if exists.",
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
"title": "Quickshell caelestia",
|
|
613
|
+
"config": """[qs-caelestia]
|
|
614
|
+
paths = [".config/quickshell/caelestia"]
|
|
615
|
+
on_activate = "qs -c caelestia"
|
|
616
|
+
on_deactivate = "pkill qs -9" """,
|
|
617
|
+
"explanation": "Quickshell with config 'caelestia'. Auto-detected if exists.",
|
|
618
|
+
},
|
|
619
|
+
{
|
|
620
|
+
"title": "Quickshell custom",
|
|
621
|
+
"config": """[qs-my-config]
|
|
622
|
+
paths = [".config/quickshell/my-config"]
|
|
623
|
+
on_activate = "qs -c my-config"
|
|
624
|
+
on_deactivate = "pkill qs -9" """,
|
|
625
|
+
"explanation": "Replace 'my-config' with your quickshell config name.",
|
|
626
|
+
},
|
|
627
|
+
{
|
|
628
|
+
"title": "Full shell setup",
|
|
629
|
+
"config": """[shell]
|
|
630
|
+
paths = [".bashrc", ".zshrc", ".config/fish"]
|
|
631
|
+
post_deploy = "shell_reload"
|
|
632
|
+
|
|
633
|
+
[vim]
|
|
634
|
+
paths = [".config/nvim"]
|
|
635
|
+
|
|
636
|
+
[tmux]
|
|
637
|
+
paths = [".tmux.conf"]
|
|
638
|
+
post_deploy = "tmux source-file ~/.tmux.conf" """,
|
|
639
|
+
"explanation": "Complete shell setup with multiple sections. "
|
|
640
|
+
"Run 'dot-man config detect' to auto-detect what's available.",
|
|
641
|
+
},
|
|
642
|
+
],
|
|
643
|
+
},
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
if section not in examples:
|
|
647
|
+
ui.error(f"Unknown section: {section}", exit_code=0)
|
|
648
|
+
ui.console.print(f"Available sections: {', '.join(examples.keys())}")
|
|
649
|
+
return
|
|
650
|
+
|
|
651
|
+
data = examples[section]
|
|
652
|
+
|
|
653
|
+
ui.console.print()
|
|
654
|
+
ui.console.print(
|
|
655
|
+
Panel.fit(
|
|
656
|
+
f"[bold blue]{data['title']}[/bold blue]\n\n{data['description']}",
|
|
657
|
+
title=f"📖 {data['title']}",
|
|
658
|
+
)
|
|
659
|
+
)
|
|
660
|
+
|
|
661
|
+
for i, example in enumerate(data["examples"], 1):
|
|
662
|
+
ui.console.print(f"\n[bold cyan]Example {i}: {example['title']}[/bold cyan]")
|
|
663
|
+
|
|
664
|
+
if "config" in example:
|
|
665
|
+
ui.console.print(
|
|
666
|
+
Syntax(example["config"], "toml", theme="monokai", line_numbers=False)
|
|
667
|
+
)
|
|
668
|
+
elif "command" in example:
|
|
669
|
+
ui.console.print(f"[green]$ {example['command']}[/green]")
|
|
670
|
+
|
|
671
|
+
ui.console.print(f"\n[dim]{example['explanation']}[/dim]")
|
|
672
|
+
|
|
673
|
+
ui.console.print(
|
|
674
|
+
"\n[dim]💡 Run 'dot-man config create' to add these examples to your config file[/dim]"
|
|
675
|
+
)
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
def _run_interactive_tutorial():
|
|
679
|
+
"""Run interactive step-by-step tutorial with detailed explanations."""
|
|
680
|
+
from rich.panel import Panel
|
|
681
|
+
from rich.syntax import Syntax
|
|
682
|
+
|
|
683
|
+
ui.console.print()
|
|
684
|
+
ui.console.print(
|
|
685
|
+
Panel.fit(
|
|
686
|
+
"[bold green]🎓 Interactive dot-man Configuration Tutorial[/bold green]\n\n"
|
|
687
|
+
"This interactive tutorial will guide you through configuring dot-man.\n"
|
|
688
|
+
"You'll learn what each configuration option does as we build examples.",
|
|
689
|
+
title="Welcome!",
|
|
690
|
+
)
|
|
691
|
+
)
|
|
692
|
+
|
|
693
|
+
# Track user configurations for final summary
|
|
694
|
+
user_configs = []
|
|
695
|
+
|
|
696
|
+
# Step 1: Basic files
|
|
697
|
+
ui.console.print("\n[bold cyan]📁 Step 1: Basic File Tracking[/bold cyan]")
|
|
698
|
+
ui.console.print(
|
|
699
|
+
"Every configuration section starts with [section-name] and defines what files to track."
|
|
700
|
+
)
|
|
701
|
+
|
|
702
|
+
ui.console.print("\n[bold green]✅ Example: Shell Configuration[/bold green]")
|
|
703
|
+
ui.console.print()
|
|
704
|
+
|
|
705
|
+
# Show the config with explanations
|
|
706
|
+
config_text = """[shell-config]
|
|
707
|
+
paths = ["~/.bashrc", "~/.zshrc"]
|
|
708
|
+
post_deploy = "shell_reload" """
|
|
709
|
+
|
|
710
|
+
ui.console.print(Syntax(config_text, "toml", theme="monokai"))
|
|
711
|
+
ui.console.print()
|
|
712
|
+
|
|
713
|
+
# Explain each part
|
|
714
|
+
ui.console.print(
|
|
715
|
+
"[bold cyan]🔍 [shell-config][/bold cyan] - A unique name for this group of files"
|
|
716
|
+
)
|
|
717
|
+
ui.console.print(
|
|
718
|
+
"[bold cyan]📂 paths[/bold cyan] - List of files/directories to track (supports ~ expansion)"
|
|
719
|
+
)
|
|
720
|
+
ui.console.print(
|
|
721
|
+
"[bold cyan]🚀 post_deploy[/bold cyan] - Command to run AFTER files are deployed"
|
|
722
|
+
)
|
|
723
|
+
ui.console.print(
|
|
724
|
+
"[bold cyan]🔄 shell_reload[/bold cyan] - Built-in alias that reloads bash/zsh"
|
|
725
|
+
)
|
|
726
|
+
ui.console.print(" [dim](runs: source ~/.bashrc || source ~/.zshrc)[/dim]")
|
|
727
|
+
|
|
728
|
+
ui.console.print(
|
|
729
|
+
"\n[dim]💡 Smart defaults apply automatically - you only specify what's different![/dim]"
|
|
730
|
+
)
|
|
731
|
+
|
|
732
|
+
ui.console.print("\n[dim]Press Enter to continue...[/dim]")
|
|
733
|
+
input()
|
|
734
|
+
|
|
735
|
+
ui.console.print(
|
|
736
|
+
"\n[bold green]✅ Git Config with Automatic Secret Protection:[/bold green]"
|
|
737
|
+
)
|
|
738
|
+
ui.console.print()
|
|
739
|
+
|
|
740
|
+
config_text = """[gitconfig]
|
|
741
|
+
paths = ["~/.gitconfig"]"""
|
|
742
|
+
|
|
743
|
+
ui.console.print(Syntax(config_text, "toml", theme="monokai"))
|
|
744
|
+
ui.console.print()
|
|
745
|
+
|
|
746
|
+
ui.console.print(
|
|
747
|
+
"[bold cyan]🔒 Automatic security[/bold cyan] - Git configs get special protection:"
|
|
748
|
+
)
|
|
749
|
+
ui.console.print(
|
|
750
|
+
" • [yellow]secrets_filter = true[/yellow] - Detects and redacts sensitive data"
|
|
751
|
+
)
|
|
752
|
+
ui.console.print(
|
|
753
|
+
" • [yellow]API keys, passwords, tokens[/yellow] - Automatically removed when saving"
|
|
754
|
+
)
|
|
755
|
+
ui.console.print(
|
|
756
|
+
' • [yellow]update_strategy = "replace"[/yellow] - Safe for most config files'
|
|
757
|
+
)
|
|
758
|
+
|
|
759
|
+
ui.console.print("\n[dim]Press Enter to continue...[/dim]")
|
|
760
|
+
input()
|
|
761
|
+
|
|
762
|
+
# Step 2: Directories with patterns
|
|
763
|
+
ui.console.print(
|
|
764
|
+
"\n[bold cyan]📂 Step 2: Directory Tracking with Patterns[/bold cyan]"
|
|
765
|
+
)
|
|
766
|
+
ui.console.print(
|
|
767
|
+
"When tracking directories, you can include/exclude specific files."
|
|
768
|
+
)
|
|
769
|
+
|
|
770
|
+
ui.console.print(
|
|
771
|
+
"\n[bold green]✅ Neovim Config with Smart Exclusions:[/bold green]"
|
|
772
|
+
)
|
|
773
|
+
ui.console.print()
|
|
774
|
+
|
|
775
|
+
config_text = """[nvim]
|
|
776
|
+
paths = ["~/.config/nvim"]
|
|
777
|
+
exclude = ["*.log", "plugin/packer_compiled.lua"]
|
|
778
|
+
post_deploy = "nvim_sync" """
|
|
779
|
+
|
|
780
|
+
ui.console.print(Syntax(config_text, "toml", theme="monokai"))
|
|
781
|
+
ui.console.print()
|
|
782
|
+
|
|
783
|
+
ui.console.print(
|
|
784
|
+
"[bold cyan]🎯 exclude[/bold cyan] - Patterns of files/directories to SKIP tracking"
|
|
785
|
+
)
|
|
786
|
+
ui.console.print(" • [yellow]*.log[/yellow] - Any .log files")
|
|
787
|
+
ui.console.print(
|
|
788
|
+
" • [yellow]plugin/packer_compiled.lua[/yellow] - Compiled plugin cache"
|
|
789
|
+
)
|
|
790
|
+
ui.console.print(
|
|
791
|
+
"[bold cyan]📝 Pattern syntax[/bold cyan] - Wildcards (*, **, ?) and gitignore-style"
|
|
792
|
+
)
|
|
793
|
+
ui.console.print(
|
|
794
|
+
"[bold cyan]🔄 nvim_sync[/bold cyan] - Alias: nvim --headless +PackerSync +qa"
|
|
795
|
+
)
|
|
796
|
+
|
|
797
|
+
ui.console.print(
|
|
798
|
+
'\n[dim]💡 Use ** for recursive: "**/*.tmp" matches all .tmp files in subdirs[/dim]'
|
|
799
|
+
)
|
|
800
|
+
|
|
801
|
+
user_configs.append(
|
|
802
|
+
(
|
|
803
|
+
"nvim",
|
|
804
|
+
"""[nvim]
|
|
805
|
+
paths = ["~/.config/nvim"]
|
|
806
|
+
exclude = ["*.log", "plugin/packer_compiled.lua"]
|
|
807
|
+
post_deploy = "nvim_sync" """,
|
|
808
|
+
)
|
|
809
|
+
)
|
|
810
|
+
|
|
811
|
+
ui.console.print("\n[dim]Press Enter to continue...[/dim]")
|
|
812
|
+
input()
|
|
813
|
+
|
|
814
|
+
# Step 3: Update strategies
|
|
815
|
+
ui.console.print(
|
|
816
|
+
"\n[bold cyan]🔄 Step 3: Update Strategies - How Files Are Deployed[/bold cyan]"
|
|
817
|
+
)
|
|
818
|
+
ui.console.print("Choose how dot-man handles existing files when deploying.")
|
|
819
|
+
|
|
820
|
+
# Show update strategy information
|
|
821
|
+
|
|
822
|
+
ui.console.print("\n[bold green]📋 Update Strategy Options:[/bold green]")
|
|
823
|
+
|
|
824
|
+
strategy_examples = {
|
|
825
|
+
"Safe (rename_old)": {
|
|
826
|
+
"config": 'update_strategy = "rename_old"',
|
|
827
|
+
"explanation": "• Backs up existing file as filename.bak\n• Then overwrites with new version\n• Your original file is safe if something goes wrong",
|
|
828
|
+
},
|
|
829
|
+
"Direct (replace)": {
|
|
830
|
+
"config": 'update_strategy = "replace" # Default',
|
|
831
|
+
"explanation": "• Directly overwrites existing files\n• No backup created\n• Fastest option",
|
|
832
|
+
},
|
|
833
|
+
"Conservative (ignore)": {
|
|
834
|
+
"config": 'update_strategy = "ignore"',
|
|
835
|
+
"explanation": "• Skips files that already exist\n• Never overwrites your changes\n• Good for one-time setup files",
|
|
836
|
+
},
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
for name, details in strategy_examples.items():
|
|
840
|
+
ui.console.print(f"\n[yellow]{name}:[/yellow]")
|
|
841
|
+
ui.console.print(Syntax(details["config"], "toml", theme="monokai"))
|
|
842
|
+
ui.console.print(details["explanation"])
|
|
843
|
+
|
|
844
|
+
ui.console.print("\n[dim]Press Enter to continue...[/dim]")
|
|
845
|
+
input()
|
|
846
|
+
|
|
847
|
+
# Step 4: Pre-deploy hooks
|
|
848
|
+
ui.console.print(
|
|
849
|
+
"\n[bold cyan]⚡ Step 4: Pre-Deploy Hooks - Actions Before Deployment[/bold cyan]"
|
|
850
|
+
)
|
|
851
|
+
ui.console.print("Sometimes you need to prepare before deploying files.")
|
|
852
|
+
|
|
853
|
+
ui.console.print("\n[bold green]🔧 Pre-deploy Hook Examples:[/bold green]")
|
|
854
|
+
ui.console.print()
|
|
855
|
+
|
|
856
|
+
examples = [
|
|
857
|
+
{
|
|
858
|
+
"title": "Backup important files",
|
|
859
|
+
"config": """[important-config]
|
|
860
|
+
paths = ["~/.important/app.conf"]
|
|
861
|
+
pre_deploy = "cp ~/.important/app.conf ~/.important/app.conf.backup" """,
|
|
862
|
+
"explanation": "Creates a backup before dot-man touches the file",
|
|
863
|
+
},
|
|
864
|
+
{
|
|
865
|
+
"title": "Stop services before config change",
|
|
866
|
+
"config": """[service-config]
|
|
867
|
+
paths = ["~/.config/my-service"]
|
|
868
|
+
pre_deploy = "systemctl --user stop my-service" """,
|
|
869
|
+
"explanation": "Stops the service before updating its config files",
|
|
870
|
+
},
|
|
871
|
+
]
|
|
872
|
+
|
|
873
|
+
for example in examples:
|
|
874
|
+
ui.console.print(f"[cyan]{example['title']}:[/cyan]")
|
|
875
|
+
ui.console.print(Syntax(example["config"], "toml", theme="monokai"))
|
|
876
|
+
ui.console.print(f" {example['explanation']}")
|
|
877
|
+
ui.console.print()
|
|
878
|
+
|
|
879
|
+
ui.console.print("\n[dim]Press Enter to continue...[/dim]")
|
|
880
|
+
input()
|
|
881
|
+
|
|
882
|
+
# Step 5: Templates
|
|
883
|
+
ui.console.print(
|
|
884
|
+
"\n[bold cyan]📋 Step 5: Templates - Reusable Configuration[/bold cyan]"
|
|
885
|
+
)
|
|
886
|
+
ui.console.print("Define shared settings that multiple sections can inherit.")
|
|
887
|
+
|
|
888
|
+
ui.console.print("\n[bold green]🎨 Template Example:[/bold green]")
|
|
889
|
+
ui.console.print()
|
|
890
|
+
|
|
891
|
+
config_text = """# Define a template
|
|
892
|
+
[templates.desktop-apps]
|
|
893
|
+
post_deploy = "notify-send 'Config updated'"
|
|
894
|
+
update_strategy = "rename_old"
|
|
895
|
+
|
|
896
|
+
# Use the template
|
|
897
|
+
[hyprland]
|
|
898
|
+
paths = ["~/.config/hypr"]
|
|
899
|
+
inherits = ["desktop-apps"]
|
|
900
|
+
|
|
901
|
+
[waybar]
|
|
902
|
+
paths = ["~/.config/waybar"]
|
|
903
|
+
inherits = ["desktop-apps"]
|
|
904
|
+
# Override settings if needed
|
|
905
|
+
update_strategy = "replace" """
|
|
906
|
+
|
|
907
|
+
ui.console.print(Syntax(config_text, "toml", theme="monokai"))
|
|
908
|
+
ui.console.print()
|
|
909
|
+
|
|
910
|
+
ui.console.print(
|
|
911
|
+
"[bold cyan]📋 Template definition[/bold cyan] - [templates.name] sections are reusable"
|
|
912
|
+
)
|
|
913
|
+
ui.console.print(
|
|
914
|
+
"[bold cyan]🔗 inherits[/bold cyan] - List of templates to inherit settings from"
|
|
915
|
+
)
|
|
916
|
+
ui.console.print(
|
|
917
|
+
"[bold cyan]⚡ Override behavior[/bold cyan] - Section settings override templates"
|
|
918
|
+
)
|
|
919
|
+
ui.console.print(
|
|
920
|
+
"[bold cyan]🎯 Use case[/bold cyan] - Share notifications, strategies, etc."
|
|
921
|
+
)
|
|
922
|
+
|
|
923
|
+
ui.console.print("\n[dim]Press Enter to continue...[/dim]")
|
|
924
|
+
input()
|
|
925
|
+
|
|
926
|
+
# Step 6: Terminal
|
|
927
|
+
ui.console.print("\n[bold cyan]💻 Step 6: Terminal Configuration[/bold cyan]")
|
|
928
|
+
|
|
929
|
+
ui.console.print("\n[bold green]✅ Kitty Terminal Configuration:[/bold green]")
|
|
930
|
+
ui.console.print()
|
|
931
|
+
|
|
932
|
+
config_text = """[kitty]
|
|
933
|
+
paths = ["~/.config/kitty"]
|
|
934
|
+
post_deploy = "kitty_reload" """
|
|
935
|
+
|
|
936
|
+
ui.console.print(Syntax(config_text, "toml", theme="monokai"))
|
|
937
|
+
ui.console.print()
|
|
938
|
+
|
|
939
|
+
ui.console.print(
|
|
940
|
+
"[bold cyan]🖥️ Kitty[/bold cyan] - Fast, GPU-accelerated terminal emulator"
|
|
941
|
+
)
|
|
942
|
+
ui.console.print("[bold cyan]📂 paths[/bold cyan] - Kitty configuration directory")
|
|
943
|
+
ui.console.print("[bold cyan]🚀 post_deploy[/bold cyan] - Reload command for Kitty")
|
|
944
|
+
ui.console.print(
|
|
945
|
+
"[bold cyan]🔄 kitty_reload[/bold cyan] - Sends SIGUSR1 to reload running instances"
|
|
946
|
+
)
|
|
947
|
+
|
|
948
|
+
user_configs.append(
|
|
949
|
+
(
|
|
950
|
+
"kitty",
|
|
951
|
+
"""[kitty]
|
|
952
|
+
paths = ["~/.config/kitty"]
|
|
953
|
+
post_deploy = "kitty_reload" """,
|
|
954
|
+
)
|
|
955
|
+
)
|
|
956
|
+
|
|
957
|
+
ui.console.print("\n[dim]Press Enter to continue...[/dim]")
|
|
958
|
+
input()
|
|
959
|
+
|
|
960
|
+
# Final summary
|
|
961
|
+
ui.console.print("\n[bold green]🎉 Tutorial Complete![/bold green]")
|
|
962
|
+
ui.console.print("\n[dim]You've learned about:[/dim]")
|
|
963
|
+
ui.console.print(" • 📁 Basic file and directory tracking")
|
|
964
|
+
ui.console.print(" • 🎯 Include/exclude patterns for selective tracking")
|
|
965
|
+
ui.console.print(" • 🔄 Update strategies (replace, rename_old, ignore)")
|
|
966
|
+
ui.console.print(" • ⚡ Pre/post deploy hooks for automation")
|
|
967
|
+
ui.console.print(" • 📋 Templates for reusable configuration")
|
|
968
|
+
ui.console.print(" • 🔒 Automatic secret detection and filtering")
|
|
969
|
+
|
|
970
|
+
ui.console.print("\n[dim]Next steps:[/dim]")
|
|
971
|
+
ui.console.print(
|
|
972
|
+
"[green]$ dot-man config create[/green] [dim]- Generate config file with examples[/dim]"
|
|
973
|
+
)
|
|
974
|
+
ui.console.print(
|
|
975
|
+
"[green]$ dot-man edit[/green] [dim]- Customize your configuration[/dim]"
|
|
976
|
+
)
|
|
977
|
+
ui.console.print(
|
|
978
|
+
"[green]$ dot-man config tutorial --section advanced[/green] [dim]- Learn advanced features[/dim]"
|
|
979
|
+
)
|