-
Notifications
You must be signed in to change notification settings - Fork 0
/
install.sh
executable file
·187 lines (170 loc) · 7.71 KB
/
install.sh
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
#!/usr/bin/env bash
#
# Create a symlink in user `$HOME` for each in-repo file in `./home/`.
#
# The core part of this is sort of like GNU `stow --adopt --target=$HOME home`,
# though this script has a bunch of special handling around the `bin` directory.
case "$BASH_VERSION" in # see also same check in `test.sh`
5.*) ;;
*)
echo "expecting bash 5.x but got '${BASH_VERSION}'; cautiously exiting" >&2
exit 1
;;
esac
set -euETo pipefail
shopt -s inherit_errexit
cd "$(dirname "${BASH_SOURCE[0]}")/home"
if [[ ! "$HOME" =~ ^/ ]]; then
# HOME needs to be absolute given that we just changed directories above.
echo 'HOME must be set and must be using an absolute path.' >&2
exit 1
elif [ "$(git rev-parse --is-inside-work-tree)" != 'true' ]; then
# We need git to be present and working for `git ls-tree`.
echo 'git must be installed and repository data must be present.' >&2
exit 1
fi
okayCount=0
failCount=0
copyCount=0
linkCount=0
while IFS='' read -rd '' srcPath; do
dstPath="$srcPath" # symlink most files at same path
if [[ "$srcPath" == "bin/"* ]]; then # except bin files are special
if [[ "$srcPath" == "bin/lib/"* ]]; then # do not symlink common bin modules
continue
elif [[ "$srcPath" == "bin/"?*.?* ]]; then # make scripts easier to type
dstPath="${srcPath%.*}" # strip file extension
dstPath="${dstPath//_/-}" # switch snake case to kebab case
else
echo "unexpected non-script file in bin directory" >&2
exit 1
fi
fi
dstPath="$HOME/$dstPath"
if [ -L "$dstPath" ]; then
srcReal="$(realpath "$srcPath")"
dstLink="$(readlink --canonicalize "$dstPath")"
if [ "$srcReal" == "$dstLink" ]; then
((++okayCount))
else
((++failCount))
echo "$srcPath: $dstPath already linked to $dstLink instead of $srcReal"
fi
elif [ -f "$dstPath" ]; then
((++copyCount))
echo "$srcPath: $dstPath already exists; use version control to reconcile"
echo -n "cp "
cp --verbose "$dstPath" "$srcPath"
ln --force --relative --symbolic --verbose "$srcPath" "$dstPath"
else
((++linkCount))
mkdir --parents --verbose "$(dirname "$dstPath")"
ln --relative --symbolic --verbose "$srcPath" "$dstPath"
fi
done < <(git ls-tree -rz --name-only HEAD) # only use committed files
echo
if [ "${CODESPACES-false}" == "true" ]; then
# These tweaks worked and these notes were accurate as of September 2021.
echo 'Codespaces-specific adjustments:'
# The .gitconfig on the pre-GA Codespaces (verified by this md5sum check) just
# contained a core.editor setting I didn't need, so could use .gitconfig from
# dotfiles instead. This doesn't trip anymore in the GA version of Codespaces.
if [ "$(md5sum <.gitconfig)" == "43ba1caca81a816ae18ac0857ad83b53 -" ]; then
echo '- discarding Codespaces-provided .gitconfig for dotfiles-provided one'
git checkout .gitconfig
fi
# Visual Studio Code in Codespaces collects its settings from three "scopes":
#
# 1. a "/User/settings.json" key stored via the browser's IndexedDB API that
# can optionally be synced using Settings Sync, which may be pointing at
# either the "VS Code Stable" or "Insiders" servers depending on the setup;
# 2. a "machine"/"remote" `~/.vscode-remote/data/Machine/settings.json`, which
# is actually intended to be used with an in-repo `devcontainer.json`; and
# 3. the in-repo `.vscode/settings.json`, if any.
#
# The desktop-standard `~/.config/Code/User/settings.json` from the dotfiles
# is not "currently" used.
#
# Alternatively, Settings Sync could be used for these settings, probably
# still keeping them version-controlled in the dotfiles repository via regular
# desktop machines that don't use the IndexedDB API for storage.
readonly codespacesSettings=~/.vscode-remote/data/Machine
readonly dotfilesSettings=.config/Code/User
if [ -d "$codespacesSettings" ] && [ ! -L "$codespacesSettings" ]; then
if [ -e "$codespacesSettings/settings.json" ]; then
if [ -f "$codespacesSettings/settings.json" ] &&
[ ! -L "$codespacesSettings/settings.json" ]; then
# "machine"/"remote" settings already exist, so merge dotfiles version
# with the Codespaces version, preferring settings from the latter; this
# method can propagate dotfiles settings additions, but requires manual
# intervention to propagate settings *changes*, since the original value
# will be in the "machine"/"remote" settings file and thus will persist
echo "- merging machine and dotfiles settings"
jq --slurp add \
<(npx --package relaxed-json -- rjson "$dotfilesSettings/settings.json") \
<(npx --package relaxed-json -- rjson "$codespacesSettings/settings.json") |
sponge "$codespacesSettings/settings.json"
fi
else
# "machine"/"remote" settings don't already exist, so symlink can just be
# created from "machine"/"remote" settings location to dotfiles version
echo -n "- linking machine settings to dotfiles: "
ln --relative --symbolic --verbose \
"$dotfilesSettings/settings.json" "$codespacesSettings/settings.json"
fi
fi
# Likewise, Visual Studio Code in Codespaces doesn't read the desktop-standard
# `~/.config/Code/User/keybindings.json`. Further, it only uses the IndexedDB
# storage for these, so there's no on-disk location to symlink.
#
# Again, using Settings Sync could probably be used for these. When using
# Settings Sync, keybindings are synchronized by platform by default, but I'm
# not sure if "web browser on Linux" counts as "linux" or as "web". If keeping
# to platforms like Linux and Windows, keybindings should be platform-agnostic
# and the `settingsSync.keybindingsPerPlatform` setting could be disabled.
# Alternatively, even without fully enabling Settings Sync across all devices,
# Settings Sync can be enabled just in the Codespaces environment, effectively
# creating a "'web-platform' keybindings" file.
echo '- Visual Studio Code keybindings.json cannot be setup here'
# ~/.profile on Ubuntu will usually handle this, but because Codespaces are
# durable and ~/bin doesn't exist when the container is setup, there is never
# an opportunity for ~/.profile to run again. In lieu of ~/.profile, this is
# the best way to get ~/bin in the PATH that I can think of right now.
# shellcheck disable=SC2016 # use unexpanded $HOME literally in strings below
if [[ ! ":$PATH:" == *":$HOME/bin:"* ]] &&
! grep --fixed-strings --quiet '$HOME/bin:$PATH' ~/.bashrc; then
echo '- modifying .bashrc to include ~/bin in the user PATH'
echo 'PATH="$HOME/bin:$PATH"' >>~/.bashrc
fi
echo
fi
# Bash will truncate history immediately upon assignment, even if number is
# adjusted upward later by `~/.bash_behavior`, so comment out such assignments
# in `~/.bashrc` if we see them.
if grep -qE '^HIST(FILE)?SIZE=[0-9]+' ~/.bashrc; then
echo 'Commenting out history size variables in ~/.bashrc'
sed -Ei 's/^(HIST(FILE)?SIZE=[0-9]+)/# \1/' ~/.bashrc
echo
fi
if [[ -t 0 && -t 1 ]]; then # stdin and stdout are both the terminal
readonly pager=${PAGER-less} # default to `less` if $PAGER not set at all
else # in pipeline or redirection
readonly pager=''
fi
(
cat <<EOF
Summary
- already installed: $okayCount
- failed to install: $failCount
- needs reconciling: $copyCount
- cleanly installed: $linkCount
EOF
echo
cat <<'EOF'
Hints
- if this is a new/newish install, a logout/login cycle might be helpful
(e.g. for ~/.profile to see ~/bin and add it to the $PATH)
- you can run update-bash-completion for common tools that might be installed
- you can run ./test.sh as a sanity check of the installation
EOF
) | ${pager:-cat}