Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
File renamed without changes.
File renamed without changes.
79 changes: 79 additions & 0 deletions home/bin/dol
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,83 @@ parse_common_flags() {
done
}

managed_block_start="# >>> ~/.dotfiles/install.sh >>>"
managed_block_end="# <<< ~/.dotfiles/install.sh <<<"

ensure_local_dotfile() {
local target="$1"

if [[ -d "${target}" ]]; then
echo "Cannot manage ${target}: it is a directory."
return 1
fi

# Older installs symlinked these files directly; replace with a fresh local file.
if [[ -L "${target}" ]]; then
echo " Replacing symlinked $(basename "${target}") with a local file..."
rm -f "${target}"
: > "${target}"
return 0
fi

if [[ ! -e "${target}" ]]; then
: > "${target}"
fi
}

strip_managed_block() {
local source_file="$1"
local destination_file="$2"

awk -v start="${managed_block_start}" -v end="${managed_block_end}" '
$0 == start {
in_block=1
next
}
in_block && $0 == end {
in_block=0
next
}
!in_block {
print
}
' "${source_file}" > "${destination_file}"
}

prepend_managed_block() {
local target="$1"
shift
local block_lines=("$@")
local stripped_file
local line

ensure_local_dotfile "${target}"

stripped_file="$(mktemp "${TMPDIR:-/tmp}/dotfiles-install.XXXXXX")"
strip_managed_block "${target}" "${stripped_file}"

{
printf '%s\n' "${managed_block_start}"
for line in "${block_lines[@]}"; do
printf '%s\n' "${line}"
done
printf '%s\n' "${managed_block_end}"
if [[ -s "${stripped_file}" ]]; then
printf '\n'
cat "${stripped_file}"
fi
} > "${target}"

rm -f "${stripped_file}"
}

configure_managed_dotfiles() {
echo "Configuring managed include blocks..."
prepend_managed_block "${HOME}/.zshrc" "[ -f \"\$HOME/.zshrc.dolan\" ] && . \"\$HOME/.zshrc.dolan\""
prepend_managed_block "${HOME}/.zshenv" "[ -f \"\$HOME/.zshenv.dolan\" ] && . \"\$HOME/.zshenv.dolan\""
prepend_managed_block "${HOME}/.gitconfig" '[include]' ' path = ~/.gitconfig.dolan'
}

run_dotfiles_install() {
local home_src
local home_dst
Expand Down Expand Up @@ -165,6 +242,8 @@ run_dotfiles_install() {
ln -sf "${file}" "${dest_file}"
done

configure_managed_dotfiles

os_name="$(uname)"
if [[ "${os_name}" == "Darwin" ]]; then
echo "Installing Mac OS Software..."
Expand Down
156 changes: 156 additions & 0 deletions tests/install.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
#!/usr/bin/env bats

repo_root="$(git rev-parse --show-toplevel)"

setup() {
tmp_home="$(mktemp -d "${TMPDIR:-/tmp}/dotfiles-install-test.XXXXXX")"
fake_bin="${tmp_home}/fake-bin"
mkdir -p "${fake_bin}" "${tmp_home}/.oh-my-zsh"

cat > "${fake_bin}/uname" <<'EOF'
#!/bin/sh
echo "Linux"
EOF
chmod +x "${fake_bin}/uname"

real_git="$(command -v git)"
cat > "${fake_bin}/git" <<EOF
#!/bin/sh
if [ "\$1" = "ls-remote" ]; then
exit 1
fi
exec "${real_git}" "\$@"
EOF
chmod +x "${fake_bin}/git"

install_env=(
"HOME=${tmp_home}"
"PATH=${fake_bin}:${PATH}"
"DOLAN_USE_HERMIT=false"
)

if command -v zsh >/dev/null 2>&1; then
install_command=(
"$(command -v zsh)"
"${repo_root}/install.sh"
)
else
install_command=(
"$(command -v bash)"
"${repo_root}/home/bin/dol"
install
)
fi
}

teardown() {
rm -rf "${tmp_home}"
}

@test "fresh install prepends managed include blocks" {
run env "${install_env[@]}" "${install_command[@]}"
[ "${status}" -eq 0 ]

[ -L "${tmp_home}/.zshrc.dolan" ]
[ -L "${tmp_home}/.zshenv.dolan" ]
[ -L "${tmp_home}/.gitconfig.dolan" ]

[ -f "${tmp_home}/.zshrc" ]
[ ! -L "${tmp_home}/.zshrc" ]
[ -f "${tmp_home}/.zshenv" ]
[ ! -L "${tmp_home}/.zshenv" ]
[ -f "${tmp_home}/.gitconfig" ]
[ ! -L "${tmp_home}/.gitconfig" ]

run sed -n '1,3p' "${tmp_home}/.zshrc"
[ "${status}" -eq 0 ]
[ "${lines[0]}" = "# >>> ~/.dotfiles/install.sh >>>" ]
[ "${lines[1]}" = "[ -f \"\$HOME/.zshrc.dolan\" ] && . \"\$HOME/.zshrc.dolan\"" ]
[ "${lines[2]}" = "# <<< ~/.dotfiles/install.sh <<<" ]

run sed -n '1,3p' "${tmp_home}/.zshenv"
[ "${status}" -eq 0 ]
[ "${lines[0]}" = "# >>> ~/.dotfiles/install.sh >>>" ]
[ "${lines[1]}" = "[ -f \"\$HOME/.zshenv.dolan\" ] && . \"\$HOME/.zshenv.dolan\"" ]
[ "${lines[2]}" = "# <<< ~/.dotfiles/install.sh <<<" ]

run sed -n '1,4p' "${tmp_home}/.gitconfig"
[ "${status}" -eq 0 ]
[ "${lines[0]}" = "# >>> ~/.dotfiles/install.sh >>>" ]
[ "${lines[1]}" = "[include]" ]
[ "${lines[2]}" = " path = ~/.gitconfig.dolan" ]
[ "${lines[3]}" = "# <<< ~/.dotfiles/install.sh <<<" ]
}

@test "existing file contents are preserved under prepended block" {
cat > "${tmp_home}/.zshrc" <<'EOF'
export KEEP_THIS_LINE=1
EOF

run env "${install_env[@]}" "${install_command[@]}"
[ "${status}" -eq 0 ]

run tail -n 1 "${tmp_home}/.zshrc"
[ "${status}" -eq 0 ]
[ "${output}" = "export KEEP_THIS_LINE=1" ]
}

@test "stale managed blocks are replaced with current include content" {
cat > "${tmp_home}/.zshrc" <<'EOF'
# >>> ~/.dotfiles/install.sh >>>
[ -f "$HOME/.old-zshrc" ] && . "$HOME/.old-zshrc"
# <<< ~/.dotfiles/install.sh <<<

export STILL_HERE=1
EOF

run env "${install_env[@]}" "${install_command[@]}"
[ "${status}" -eq 0 ]

run grep -c '^# >>> ~/.dotfiles/install.sh >>>$' "${tmp_home}/.zshrc"
[ "${status}" -eq 0 ]
[ "${output}" -eq 1 ]

run grep -c 'old-zshrc' "${tmp_home}/.zshrc"
[ "${status}" -eq 1 ]

run sed -n '1,3p' "${tmp_home}/.zshrc"
[ "${status}" -eq 0 ]
[ "${lines[1]}" = "[ -f \"\$HOME/.zshrc.dolan\" ] && . \"\$HOME/.zshrc.dolan\"" ]
}

@test "existing symlinked dotfiles are unlinked and replaced with local files" {
ln -s "${repo_root}/home/.zshrc.dolan" "${tmp_home}/.zshrc"
ln -s "${repo_root}/home/.zshenv.dolan" "${tmp_home}/.zshenv"
ln -s "${repo_root}/home/.gitconfig.dolan" "${tmp_home}/.gitconfig"

run env "${install_env[@]}" "${install_command[@]}"
[ "${status}" -eq 0 ]

[ -f "${tmp_home}/.zshrc" ]
[ ! -L "${tmp_home}/.zshrc" ]
[ -f "${tmp_home}/.zshenv" ]
[ ! -L "${tmp_home}/.zshenv" ]
[ -f "${tmp_home}/.gitconfig" ]
[ ! -L "${tmp_home}/.gitconfig" ]
}

@test "re-running install does not duplicate managed blocks" {
run env "${install_env[@]}" "${install_command[@]}"
[ "${status}" -eq 0 ]

run env "${install_env[@]}" "${install_command[@]}"
[ "${status}" -eq 0 ]

run grep -c '^# >>> ~/.dotfiles/install.sh >>>$' "${tmp_home}/.zshrc"
[ "${status}" -eq 0 ]
[ "${output}" -eq 1 ]

run grep -c '^# >>> ~/.dotfiles/install.sh >>>$' "${tmp_home}/.zshenv"
[ "${status}" -eq 0 ]
[ "${output}" -eq 1 ]

run grep -c '^# >>> ~/.dotfiles/install.sh >>>$' "${tmp_home}/.gitconfig"
[ "${status}" -eq 0 ]
[ "${output}" -eq 1 ]
}
13 changes: 13 additions & 0 deletions tests/lint.bats
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ repo_root="$(git rev-parse --show-toplevel)"
check_script() {
local file="$1"
local shebang

if [[ "${file}" == *.bats ]]; then
bats --count "${file}" >/dev/null
return 0
fi

shebang="$(head -n1 "${file}")"
if [[ "${shebang}" == *zsh* ]] && command -v zsh >/dev/null 2>&1; then
zsh -n "${file}"
Expand All @@ -31,6 +37,13 @@ done < <(find "${repo_root}/tests" -name '*.bats' -type f || true)

scripts+=("${bats_tests[@]}")

# De-duplicate script paths in case a Bats test contains heredocs with shebangs.
unique_scripts=()
while IFS= read -r line; do
unique_scripts+=("${line}")
done < <(printf '%s\n' "${scripts[@]}" | awk 'NF && !seen[$0]++')
scripts=("${unique_scripts[@]}")

for script in "${scripts[@]}"; do
bats_test_function --description "${script}" -- check_script "${script}"
done