~mapperr/dfl

ref: 6d55777429efe8b49bed144f6d4234a7aaa74dfa dfl/dfl -rwxr-xr-x 11.0 KiB
6d557774mapperr Suppress hostname command not found error 1 year, 3 months ago
                                                                                
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
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
#!/usr/bin/env bash

# DotFiles Linker

# requirements:
# - bash, I guess
# - readlink

# optionals:
# - git

# https://stackoverflow.com/a/246128/1407614
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
  SOURCE="$(readlink "$SOURCE")"
  [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
SCRIPT_DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"

script_name="dfl"
script_url="https://raw.githubusercontent.com/mapperr/dfl/master/dfl"

dotfiles_dir="dotfiles"
default_profile_name="default"
secrets_profile_name="secrets"
track_filename=".track"
target_directory="$HOME"
debug="false"

dfl_user_dir_name=".dfl"
dfl_user_dir_abs="$HOME/$dfl_user_dir_name"
dotfiles_dir_abs="$dfl_user_dir_abs/$dotfiles_dir"
track_file_abs="$dfl_user_dir_abs/$track_filename"
untrack_file_abs="$dfl_user_dir_abs/.untrack"

update_scriptpath_abs="$dfl_user_dir_abs/update_dfl.sh"
backup_suffix="-dflbak"

environment_hostname=`hash hostname 2>/dev/null && hostname`
environment_override="$DFL_OVERRIDE"

logd() {
    local msg="$1"
    test "$debug" = "true" && echo "$msg"
}

write_update_script() {
    cat > "$update_scriptpath_abs" << EOF
#!/bin/bash
script_path_abs="$SCRIPT_DIR/$script_name"
if hash curl; then
    curl -o "\$script_path_abs" "$script_url"
    if [ \$? -ne 0 ]; then
        echo "ERROR: cannot update myself"
        exit 1
    fi
fi
echo "Updated"
EOF
}

take_file() {
    local file="$1"
    local profile="$2"

    local current_dir_abs=`pwd`

    if [ -z "$profile" ]; then
        local profile="$default_profile_name"
    fi

    if [ -L "$file" ]; then
        echo "[$file] is already a symlink, just take/move the original file" >&2
        return 1
    fi

    if [ ! -f "$file" ] && [ ! -d "$file" ]; then
        echo "[$file] is not a regular file/directory" >&2
        return 1
    fi

    if echo "$file" | grep -e "^/" -e "^~/" >/dev/null; then
        local filepath_abs="$file"
    else
        local filepath_abs="$current_dir_abs/$file"
    fi

    if ! echo "$filepath_abs" | grep "^$target_directory" >/dev/null; then
        echo "ERROR: [$filepath_abs] is not in [$target_directory]"
        return 1
    fi

    local target_relative_filepath=`echo "$filepath_abs" | sed "s#^$target_directory/##"`
    logd "target-relative filepath: [$target_relative_filepath]"

    if ! echo "$target_relative_filepath" | grep '^\.' >/dev/null; then
        echo "ERROR: [$file] is not a dotfile nor in a dotdirectory"
        return 1
    fi

    local target_relative_filepath_undotted=`echo "$target_relative_filepath" | sed 's#^\.##'`
    local profile_filepath_abs="$dotfiles_dir_abs/$profile/$target_relative_filepath_undotted"

    local profile_dir_path_abs=`dirname "$profile_filepath_abs"`

    echo -n "taking [$filepath_abs] into $profile profile as [$target_relative_filepath_undotted]... "
    test -d "$profile_dir_path_abs" || mkdir -p "$profile_dir_path_abs"
    mv "$filepath_abs" "$profile_filepath_abs"
    echo "done."
}

track_file() {
    local file_to_track="$1"
    if ! grep $file_to_track $track_file_abs >/dev/null; then
        echo "$file_to_track" >> $track_file_abs
    fi
}

process_file() {
    local profile_name="$1"
    local relative_filepath="$2"

    local absolute_source_path="$dotfiles_dir_abs/$profile_name/$relative_filepath"
    local target_filepath="$target_directory/.$relative_filepath"
    local target_dirname=`dirname $target_filepath`

    if [ ! -d "$target_dirname" ]; then
        logd "creating directory [$target_dirname]"
        mkdir -p $target_dirname
    fi

    if [ -f "$target_filepath" ] || [ -L "$target_filepath" ]; then
        logd "[$target_filepath] exists"
        if readlink "$target_filepath" > /dev/null; then
            logd "deleting existing link [$target_filepath]"
            rm -f $target_filepath
        else
            echo "WARNING: [$target_filepath] is present and is not a link"
            echo -n "do you want to replace it with a link? (I'll backup it as [$target_filepath$backup_suffix]) (y/n) "
            read c
            if [ "$c" = "y" ]; then
                mv $target_filepath $target_filepath$backup_suffix
                if [ ! $? = 0 ]; then
                    echo "ERROR: cannot backup [$target_filepath], I will not link it"
                    return
                fi
            else
                echo "ok, I wont touch it"
                return
            fi
        fi
    else
        logd "[$target_filepath] does not exists, I will link it"
    fi

    logd "linking [$absolute_source_path] to [$target_filepath]."
    ln -s $absolute_source_path $target_filepath
    track_file $target_filepath
}

process_deleted_files() {
    local profile_dir="$1"

    test -f "$untrack_file_abs" && rm -f "$untrack_file_abs"

    for tracked_file in `cat $track_file_abs`; do
        logd "processing tracked file [$tracked_file]"
        local tracked_file_relative=`echo "$tracked_file" | sed "s_$target_directory/\.__"`
        local found="false"
        for directory in "$default_profile_name" "$secrets_profile_name" "$profile_dir"; do
            test -z "$directory" && continue
            local directory_abs="$dotfiles_dir_abs/$directory"
            test ! -d $directory_abs && continue
            local filepath="$directory_abs/$tracked_file_relative"
            logd "checking if file [$filepath] exists"
            if [ -f "$filepath" ]; then
                logd "found [$filepath]"
                found="true"
                continue
            fi
        done
        if [ "$found" = "false" ]; then
            echo "deleting link [$tracked_file]"
            rm -f "$tracked_file"
            echo "$tracked_file" >> "$untrack_file_abs"
        fi
    done

    if [ -f "$untrack_file_abs" ]; then
        grep -vf "$untrack_file_abs" "$track_file_abs" > "$track_file_abs~"
        mv "$track_file_abs~" "$track_file_abs"
        rm -f "$untrack_file_abs"
    fi
}

process_subdirectory() {
    local profile_name="$1"
    local directory="$2"

    if [ -z "$directory" ]; then
        echo "argument directory is blank" >&2
        exit 1
    fi

    logd "processing subdirectory $directory"

    local relative_path="$profile_name/$directory"

    for item in `/bin/ls -A $relative_path`; do
        local item_path="$relative_path/$item"
        logd "processing item [$item_path]"

        if [ -d "$item_path" ]; then
            logd "$item is a directory"
            process_subdirectory "$profile_name" "$directory/$item"
        fi

        if [ -f "$item_path" ]; then
            process_file $profile_name $directory/$item
        fi
    done
}

process_profile() {
    local profile_name="$1"

    if [ -z "$profile_name" ]; then
        echo "argument profile_name is blank" >&2
        exit 1
    fi

    logd "processing directory $profile_name"

    for item in `/bin/ls -A $profile_name`; do

        if [ -d "$profile_name/$item" ]; then
            logd "$item is a directory"
            process_subdirectory "$profile_name" "$item"
        fi

        if [ -f "$profile_name/$item" ]; then
            process_file $profile_name $item
        fi
    done
}

helpmsg() {
    echo "Usage: `basename $0` [-v] <command>
Commands:
    l, link [profile_name]
        links dotfiles in default, secrets and, eventually,
        profile_name directories, in this order

    t, take filename|dirname [profile_name]
        move the file (or directory) into your dotfiles,
        in the specified profile directory, or in default if its omitted

    g, git [git_arguments]
        runs git commands in the dotfiles directory

    clone <git repository>
        clone a git repo in the dotfile directory,
        backupping the eventual existing one

    d
        prints the dotfiles directory absolute path

    u, up, update
        self update from [$script_url]

Options:
    -v  -  activate verbose mode

Auto profile:
    you can make dfl choose a profile automatically so that you don't have
    to remember to type it if you set the env var DFL_OVERRIDE.
    If dfl does not find that var, it checks if there is a profile dir named
    as the output of the hostname command and take that as the profile.
"
}

# -------

if [ ! -d "$dfl_user_dir_abs" ]; then
    mkdir -p "$dfl_user_dir_abs"
fi

command="$1"

if [ "$command" = "-v" ]; then
    debug=true
    shift
    command="$1"
fi


if [ "$command" = "g" ] || [ "$command" = "git" ]; then

    shift
    if [ ! -d $dotfiles_dir_abs ]; then
        echo "I cannot find the dotfiles directory [$dotfiles_dir_abs]"
        echo "You can clone your dotfiles repo with 'dfl clone'"
        echo "or copy some dotfiles in [$dotfiles_dir_abs/default]"
        exit 1
    fi
    cd $dotfiles_dir_abs > /dev/null
    git $@
    cd - >/dev/null

elif [ "$command" = "cd" ] || [ "$command" = "j" ]; then

    cd "$dotfiles_dir_abs"

elif [ "$command" = "d" ]; then

    echo "$dotfiles_dir_abs"

elif [ "$command" = "clone" ]; then

    shift
    repo="$1"
    if [ -d "$dotfiles_dir_abs" ]; then
        echo -n "Backupping dotfiles current dir... "
        mv $dotfiles_dir_abs $dotfiles_dir_abs$backup_suffix
        if [ $? -ne 0 ]; then
            echo "ERROR: Cannot backup $directory_abs, aborting" >&2
            exit 1
        fi
        echo "done."
    fi
    cd $dfl_user_dir_abs >/dev/null
    dotfiles_cloned_dir="dotfiles-cloned"
    git clone "$repo" $dotfiles_cloned_dir
    if [ $? -ne 0 ]; then
        echo "ERROR: Cannot clone repo [$repo], rollbacking" >&2
        rm -rf $dotfiles_cloned_dir
        mv $dotfiles_dir_abs$backup_suffix $dotfiles_dir_abs
        exit 1
    fi
    mv $dotfiles_cloned_dir $dotfiles_dir
    cd - >/dev/null

elif [ "$command" = "t" ] || [ "$command" = "take" ]; then

    shift
    file="$1"
    profile="$2"
    take_file "$file" "$profile"

elif [ "$command" = "l" ] || [ "$command" = "link" ]; then

    shift
    profile="$1"
    if [ ! -d $dotfiles_dir_abs ]; then
        echo "I cannot find the dotfiles directory [$dotfiles_dir_abs]"
        echo "You can clone your dotfiles repo with 'dfl clone'"
        echo "or copy some dotfiles in [$dotfiles_dir_abs/default]"
        exit 1
    fi
    cd $dotfiles_dir_abs > /dev/null
    test -d $default_profile_name && process_profile $default_profile_name
    if [ -d "$profile" ]; then
        process_profile "$profile"
    else
        if [ -d "$environment_override" ]; then
            process_profile "$environment_override"
            profile="$environment_override"
        elif [ -d "$environment_hostname" ]; then
            process_profile "$environment_hostname"
            profile="$environment_hostname"
        fi
    fi
    test -d $secrets_profile_name && process_profile $secrets_profile_name
    cd - >/dev/null
    process_deleted_files $profile

elif [ "$command" = "u" ] || [ "$command" = "up" ] || [ "$command" = "update" ]; then

    write_update_script
    exec /bin/bash "$update_scriptpath_abs"

else
    helpmsg
fi