-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathoffload
More file actions
executable file
·315 lines (264 loc) · 7.86 KB
/
Copy pathoffload
File metadata and controls
executable file
·315 lines (264 loc) · 7.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
#!/usr/bin/env bash
# Offload script - Convert symlinks to regular files/directories
# This is the reverse of dotbot's "link" directive
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
BASEDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BACKUP_DIR="/tmp/dotfiles_offload_$(date +%Y%m%d_%H%M%S)"
CONFIG="install.conf.yaml"
# Function to print colored output
print_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Function to show usage
usage() {
cat << EOF
Usage: $0 [options] [files...]
Offload dotfiles from version control by converting symlinks to regular files.
Arguments:
files... One or more file/directory names (without path or leading dot)
Examples: emacs flake8 zsh/aliases.zsh
Options:
-h, --help Show this help message
-l, --list List all managed symlinks from install.conf.yaml
-b, --backup DIR Use specific backup directory (default: /tmp/dotfiles_offload_TIMESTAMP)
-c, --config FILE Use specific config file (default: install.conf.yaml)
-d, --dry-run Show what would be done without making changes
-y, --yes Skip confirmation prompts
Examples:
# Offload top-level config files
$0 emacs flake8 pythonrc
# Offload config directories (auto-detected as ~/.config/*)
$0 alacritty nvim pip
# List all managed files
$0 --list
# Dry run to see what would happen
$0 --dry-run nvim
EOF
exit 0
}
# Function to parse install.conf.yaml and extract link targets
list_managed_files() {
print_info "Managed files from $CONFIG:"
echo ""
# Extract link targets from YAML
# Handles both simple (~/.vimrc: vimrc) and extended syntax
grep -E "^\s*~/" "$BASEDIR/$CONFIG" 2>/dev/null | \
grep ":" | \
sed 's/://g' | \
sed 's/^[[:space:]]*//' | \
sort | while read -r link; do
# Skip config glob entries
if [[ "$link" == */\* ]]; then
continue
fi
# Expand ~ to $HOME for checking
expanded_link="${link/#\~/$HOME}"
# Check if it exists and is a symlink
if [ -L "$expanded_link" ]; then
target=$(readlink "$expanded_link")
echo -e " ${GREEN}✓${NC} $link → $target"
elif [ -e "$expanded_link" ]; then
echo -e " ${YELLOW}~${NC} $link (real file, already offloaded?)"
else
echo -e " ${RED}✗${NC} $link (not found)"
fi
done
}
# Function to check if a file is managed in install.conf.yaml
is_managed() {
local file="$1"
# Check if it's explicitly managed as top-level (e.g., ~/.vimrc:)
if grep -q "^~/\.${file}:" "$BASEDIR/$CONFIG" 2>/dev/null; then
echo "top"
return 0
fi
# Check if it's explicitly managed in config (e.g., ~/.config/nvim:)
if grep -q "^~/\.config/${file}:" "$BASEDIR/$CONFIG" 2>/dev/null; then
echo "config"
return 0
fi
return 1
}
# Function to offload a single file/directory
offload_item() {
local item="$1"
local home_path=""
local repo_path=""
local config_item=false
# Determine if this is a config item or top-level item
# Check top-level first (e.g., ~/.vimrc)
if [ -e "$HOME/.$item" ]; then
home_path="$HOME/.$item"
repo_path="$BASEDIR/$item"
# Check in ~/.config/ (e.g., ~/.config/nvim)
elif [ -e "$HOME/.config/$item" ]; then
home_path="$HOME/.config/$item"
repo_path="$BASEDIR/config/$item"
config_item=true
else
print_error "$item: Not found in home directory or ~/.config/"
return 1
fi
# Check if it's a symlink
if [ ! -L "$home_path" ]; then
print_warn "$item: Not a symlink (already offloaded?)"
return 0
fi
# Check if it points to the repo
local target=$(readlink "$home_path")
if [[ "$target" != "$BASEDIR"* ]]; then
print_warn "$item: Symlink doesn't point to dotfiles repo (points to $target)"
return 0
fi
local display_path=".$item"
if [ "$config_item" = true ]; then
display_path=".config/$item"
fi
print_info "Offloading $display_path..."
if [ "$DRY_RUN" = true ]; then
echo " Would copy: $home_path → $BACKUP_DIR/$item"
echo " Would remove symlink: $home_path"
echo " Would restore: $BACKUP_DIR/$item → $home_path"
echo " Would remove from repo: $repo_path"
return 0
fi
# Backup and offload
cp -rL "$home_path" "$BACKUP_DIR/$item"
rm "$home_path"
mv "$BACKUP_DIR/$item" "$home_path"
# Remove from repo if it exists there
if [ -e "$repo_path" ]; then
rm -rf "$repo_path"
print_info " Removed from repo: $item"
fi
print_info " ✓ Offloaded $display_path"
return 0
}
# Function to remove entry from install.conf.yaml
remove_from_config() {
local item="$1"
local item_type=$(is_managed "$item")
if [ "$DRY_RUN" = true ]; then
echo " Would remove $item from $CONFIG"
return 0
fi
# Create backup of config
cp "$BASEDIR/$CONFIG" "$BASEDIR/${CONFIG}.backup"
# Remove the entry based on type
if [ "$item_type" = "config" ]; then
# Remove config entry (e.g., ~/.config/nvim:)
# Handle both simple and extended syntax
perl -i -0777 -pe "s/(\s*)~\/\.config\/${item}:\s*\n(\s*(path:|relink:|force:|create:).*\n)*//gs" "$BASEDIR/$CONFIG"
else
# Remove top-level entry (e.g., ~/.vimrc:)
# Handle both simple and extended syntax
perl -i -0777 -pe "s/(\s*)~\/\.${item}:\s*\n(\s*(path:|relink:|force:|create:).*\n)*//gs" "$BASEDIR/$CONFIG"
fi
print_info " ✓ Removed $item from $CONFIG"
print_info " Backup saved to ${CONFIG}.backup"
}
# Parse arguments
DRY_RUN=false
SKIP_CONFIRM=false
LIST_ONLY=false
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
usage
;;
-l|--list)
LIST_ONLY=true
shift
;;
-b|--backup)
BACKUP_DIR="$2"
shift 2
;;
-c|--config)
CONFIG="$2"
shift 2
;;
-d|--dry-run)
DRY_RUN=true
shift
;;
-y|--yes)
SKIP_CONFIRM=true
shift
;;
-*)
print_error "Unknown option: $1"
usage
;;
*)
break
;;
esac
done
# Create backup directory
mkdir -p "$BACKUP_DIR"
print_info "Backup directory: $BACKUP_DIR"
# List mode
if [ "$LIST_ONLY" = true ]; then
list_managed_files
exit 0
fi
# Check if files were specified
if [ $# -eq 0 ]; then
print_error "No files specified. Use --list to see managed files, or --help for usage."
exit 1
fi
# Show what will be offloaded
print_info "Files to offload:"
for item in "$@"; do
echo " - $item"
done
# Confirm unless --yes was specified
if [ "$SKIP_CONFIRM" = false ] && [ "$DRY_RUN" = false ]; then
echo ""
read -p "Continue? [y/N] " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
print_info "Aborted"
exit 0
fi
fi
# Offload each item
FAILED_ITEMS=()
for item in "$@"; do
if ! offload_item "$item"; then
FAILED_ITEMS+=("$item")
fi
done
# Remove from config
if [ "$DRY_RUN" = false ]; then
echo ""
print_info "Removing entries from $CONFIG..."
for item in "$@"; do
if is_managed "$item"; then
remove_from_config "$item"
fi
done
fi
# Summary
echo ""
print_info "Offload complete!"
print_info "Backup saved to: $BACKUP_DIR"
if [ ${#FAILED_ITEMS[@]} -gt 0 ]; then
print_warn "Failed to offload: ${FAILED_ITEMS[*]}"
exit 1
fi
if [ "$DRY_RUN" = false ]; then
print_info "Run './install' to clean up any broken symlinks"
fi