mirror of
https://github.com/prdlk/dotfiles.git
synced 2026-11-02 19:32:43 +00:00
396 lines
9.4 KiB
Bash
Executable File
396 lines
9.4 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
# dots.prd.lk/install — chezmoi install script, preset for prdlk/dotfiles.
|
|
#
|
|
# Vendored from https://get.chezmoi.io/lb with three changes:
|
|
# 1. BINDIR defaults to $HOME/.local/bin (absolute, not cwd-relative).
|
|
# 2. With no chezmoi-args, runs `chezmoi init --apply $DOTFILES_REPO`
|
|
# (default prdlk/dotfiles).
|
|
# 3. chezmoi's stdin is re-attached to /dev/tty when this script is piped
|
|
# into sh, so first-run prompts (name, email, role) still work.
|
|
#
|
|
# Usage:
|
|
# curl -fsSL https://dots.prd.lk/install | sh
|
|
# sh -c "$(curl -fsSL https://dots.prd.lk/install)" -- init --apply --ssh prdlk/dotfiles
|
|
|
|
# chezmoi install script
|
|
# contains code from and inspired by
|
|
# https://github.com/client9/shlib
|
|
# https://github.com/goreleaser/godownloader
|
|
|
|
# Copyright (C) 2017 Nick Galbreath
|
|
# Copyright (C) 2018 Tom Payne
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
set -e
|
|
|
|
BINDIR="${BINDIR:-${HOME}/.local/bin}"
|
|
DOTFILES_REPO="${DOTFILES_REPO:-prdlk/dotfiles}"
|
|
TAGARG=latest
|
|
LOG_LEVEL=2
|
|
|
|
tmpdir="$(mktemp -d)"
|
|
trap 'rm -rf -- "${tmpdir}"' EXIT
|
|
trap 'exit' INT TERM
|
|
|
|
usage() {
|
|
this="${1}"
|
|
cat <<EOF
|
|
${this}: download chezmoi and optionally run chezmoi
|
|
|
|
Usage: ${this} [-b bindir] [-d] [-t tag] [chezmoi-args]
|
|
-b sets the installation directory, default is ${BINDIR}.
|
|
-d enables debug logging.
|
|
-t sets the tag, default is ${TAGARG}.
|
|
After install chezmoi is executed with chezmoi-args, which default to
|
|
'init --apply ${DOTFILES_REPO}' (override the repo with DOTFILES_REPO).
|
|
EOF
|
|
exit 2
|
|
}
|
|
|
|
main() {
|
|
parse_args "${@}"
|
|
shift "$((OPTIND - 1))"
|
|
|
|
GOOS="$(get_goos)"
|
|
GOARCH="$(get_goarch)"
|
|
check_goos_goarch "${GOOS}/${GOARCH}"
|
|
|
|
TAG="$(real_tag "${TAGARG}")"
|
|
VERSION="${TAG#v}"
|
|
|
|
log_info "found chezmoi version ${VERSION} for ${TAGARG}/${GOOS}/${GOARCH}"
|
|
|
|
BINSUFFIX=
|
|
FORMAT=tar.gz
|
|
GOOS_EXTRA=
|
|
case "${GOOS}" in
|
|
linux)
|
|
case "${GOARCH}" in
|
|
amd64)
|
|
case "$(get_libc)" in
|
|
glibc)
|
|
GOOS_EXTRA="-glibc"
|
|
;;
|
|
musl)
|
|
GOOS_EXTRA="-musl"
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|
|
;;
|
|
windows)
|
|
BINSUFFIX=.exe
|
|
FORMAT=zip
|
|
;;
|
|
esac
|
|
case "${GOARCH}" in
|
|
386) arch=i386 ;;
|
|
*) arch="${GOARCH}" ;;
|
|
esac
|
|
|
|
# download tarball
|
|
NAME="chezmoi_${VERSION}_${GOOS}${GOOS_EXTRA}_${arch}"
|
|
TARBALL="${NAME}.${FORMAT}"
|
|
TARBALL_URL="https://github.com/twpayne/chezmoi/releases/download/${TAG}/${TARBALL}"
|
|
http_download "${tmpdir}/${TARBALL}" "${TARBALL_URL}" || exit 1
|
|
|
|
# download checksums
|
|
CHECKSUMS="chezmoi_${VERSION}_checksums.txt"
|
|
CHECKSUMS_URL="https://github.com/twpayne/chezmoi/releases/download/${TAG}/${CHECKSUMS}"
|
|
http_download "${tmpdir}/${CHECKSUMS}" "${CHECKSUMS_URL}" || exit 1
|
|
|
|
# verify checksums
|
|
hash_sha256_verify "${tmpdir}/${TARBALL}" "${tmpdir}/${CHECKSUMS}"
|
|
|
|
(cd -- "${tmpdir}" && untar "${TARBALL}")
|
|
|
|
# install binary
|
|
if [ ! -d "${BINDIR}" ]; then
|
|
install -d "${BINDIR}"
|
|
fi
|
|
BINARY="chezmoi${BINSUFFIX}"
|
|
install -- "${tmpdir}/${BINARY}" "${BINDIR}/"
|
|
log_info "installed ${BINDIR}/${BINARY}"
|
|
|
|
if [ -z "${1+n}" ]; then
|
|
set -- init --apply "${DOTFILES_REPO}"
|
|
fi
|
|
if [ ! -t 0 ] && (: </dev/tty) 2>/dev/null; then
|
|
exec "${BINDIR}/${BINARY}" "${@}" </dev/tty
|
|
fi
|
|
exec "${BINDIR}/${BINARY}" "${@}"
|
|
}
|
|
|
|
parse_args() {
|
|
while getopts "b:dh?t:" arg; do
|
|
case "${arg}" in
|
|
b)
|
|
|
|
if [ "${OPTARG}" = "bin" ] || [ "${OPTARG}" = "./bin" ]; then
|
|
log_info "instead of using 'get.chezmoi.io/lb -b bin', use 'get.chezmoi.io' instead"
|
|
fi
|
|
BINDIR="${OPTARG}"
|
|
;;
|
|
d) LOG_LEVEL=3 ;;
|
|
h | \?) usage "${0}" ;;
|
|
t) TAGARG="${OPTARG}" ;;
|
|
*) return 1 ;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
get_goos() {
|
|
os="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
|
case "${os}" in
|
|
cygwin_nt*) goos="windows" ;;
|
|
linux)
|
|
case "$(uname -o | tr '[:upper:]' '[:lower:]')" in
|
|
android) goos="android" ;;
|
|
*) goos="linux" ;;
|
|
esac
|
|
;;
|
|
mingw*) goos="windows" ;;
|
|
msys_nt*) goos="windows" ;;
|
|
*) goos="${os}" ;;
|
|
esac
|
|
printf '%s' "${goos}"
|
|
}
|
|
|
|
get_goarch() {
|
|
arch="$(uname -m)"
|
|
case "${arch}" in
|
|
aarch64) goarch="arm64" ;;
|
|
arm64) goarch="arm64" ;;
|
|
armv5*) goarch="armv5" ;;
|
|
arm*) goarch="armv6" ;;
|
|
i386) goarch="386" ;;
|
|
i686) goarch="386" ;;
|
|
i86pc) goarch="amd64" ;;
|
|
x86) goarch="386" ;;
|
|
x86_64) goarch="amd64" ;;
|
|
*) goarch="${arch}" ;;
|
|
esac
|
|
printf '%s' "${goarch}"
|
|
}
|
|
|
|
check_goos_goarch() {
|
|
case "${1}" in
|
|
android/arm64) return 0 ;;
|
|
darwin/amd64) return 0 ;;
|
|
darwin/arm64) return 0 ;;
|
|
freebsd/386) return 0 ;;
|
|
freebsd/amd64) return 0 ;;
|
|
freebsd/arm) return 0 ;;
|
|
freebsd/arm64) return 0 ;;
|
|
linux/386) return 0 ;;
|
|
linux/amd64) return 0 ;;
|
|
linux/armv5) return 0 ;;
|
|
linux/armv6) return 0 ;;
|
|
linux/arm64) return 0 ;;
|
|
linux/loong64) return 0 ;;
|
|
linux/mips64) return 0 ;;
|
|
linux/mips64le) return 0 ;;
|
|
linux/ppc64) return 0 ;;
|
|
linux/ppc64le) return 0 ;;
|
|
linux/riscv64) return 0 ;;
|
|
linux/s390x) return 0 ;;
|
|
openbsd/386) return 0 ;;
|
|
openbsd/amd64) return 0 ;;
|
|
openbsd/arm) return 0 ;;
|
|
openbsd/arm64) return 0 ;;
|
|
openbsd/ppc64) return 0 ;;
|
|
windows/386) return 0 ;;
|
|
windows/amd64) return 0 ;;
|
|
*)
|
|
log_crit '%s: unsupported platform\n' "${1}" 1>&2
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
get_libc() {
|
|
if is_command ldd; then
|
|
case "$(ldd --version 2>&1 | tr '[:upper:]' '[:lower:]')" in
|
|
*glibc* | *"gnu libc"*)
|
|
# If the version of glibc is too old then use the statically-linked
|
|
# musl version instead. chezmoi releases are built on GitHub Actions
|
|
# ubuntu-22.04 runners, which have glibc version 2.35.
|
|
minimum_glibc_version=2.35
|
|
glibc_version="$(ldd --version 2>&1 | awk '$1 == "ldd" { print $NF }')"
|
|
# shellcheck disable=SC2046,SC2183
|
|
minimum_glibc_version_string="$(printf "%03d%03d" $(echo "${minimum_glibc_version}" | tr "." " "))"
|
|
# shellcheck disable=SC2046,SC2183
|
|
glibc_version_string="$(printf "%03d%03d" $(echo "${glibc_version}" | tr "." " "))"
|
|
log_info "found glibc version ${glibc_version}"
|
|
if [ "${glibc_version_string}" -lt "${minimum_glibc_version_string}" ]; then
|
|
printf musl
|
|
return
|
|
fi
|
|
printf glibc
|
|
return
|
|
;;
|
|
*musl*)
|
|
printf musl
|
|
return
|
|
;;
|
|
esac
|
|
fi
|
|
if is_command getconf; then
|
|
case "$(getconf GNU_LIBC_VERSION 2>&1)" in
|
|
*glibc*)
|
|
printf glibc
|
|
return
|
|
;;
|
|
esac
|
|
fi
|
|
log_crit "unable to determine libc" 1>&2
|
|
exit 1
|
|
}
|
|
|
|
real_tag() {
|
|
tag="${1}"
|
|
log_debug "checking GitHub for tag ${tag}"
|
|
release_url="https://github.com/twpayne/chezmoi/releases/${tag}"
|
|
json="$(http_get "${release_url}" "Accept: application/json")"
|
|
if [ -z "${json}" ]; then
|
|
log_err "real_tag error retrieving GitHub release ${tag}"
|
|
return 1
|
|
fi
|
|
real_tag="$(printf '%s\n' "${json}" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')"
|
|
if [ -z "${real_tag}" ]; then
|
|
log_err "real_tag error determining real tag of GitHub release ${tag}"
|
|
return 1
|
|
fi
|
|
if [ -z "${real_tag}" ]; then
|
|
return 1
|
|
fi
|
|
log_debug "found tag ${real_tag} for ${tag}"
|
|
printf '%s' "${real_tag}"
|
|
}
|
|
|
|
http_get() {
|
|
tmpfile="$(mktemp)"
|
|
http_download "${tmpfile}" "${1}" "${2}" || return 1
|
|
body="$(cat "${tmpfile}")"
|
|
rm -f "${tmpfile}"
|
|
printf '%s\n' "${body}"
|
|
}
|
|
|
|
http_download_curl() {
|
|
local_file="${1}"
|
|
source_url="${2}"
|
|
header="${3}"
|
|
if [ -z "${header}" ]; then
|
|
code="$(curl -w '%{http_code}' -fsSL -o "${local_file}" "${source_url}")"
|
|
else
|
|
code="$(curl -w '%{http_code}' -fsSL -H "${header}" -o "${local_file}" "${source_url}")"
|
|
fi
|
|
if [ "${code}" != "200" ]; then
|
|
log_debug "http_download_curl received HTTP status ${code}"
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
http_download_wget() {
|
|
local_file="${1}"
|
|
source_url="${2}"
|
|
header="${3}"
|
|
if [ -z "${header}" ]; then
|
|
wget -q -O "${local_file}" "${source_url}" || return 1
|
|
else
|
|
wget -q --header "${header}" -O "${local_file}" "${source_url}" || return 1
|
|
fi
|
|
}
|
|
|
|
http_download() {
|
|
log_debug "http_download ${2}"
|
|
if is_command curl; then
|
|
http_download_curl "${@}" || return 1
|
|
return
|
|
elif is_command wget; then
|
|
http_download_wget "${@}" || return 1
|
|
return
|
|
fi
|
|
log_crit "http_download unable to find wget or curl"
|
|
return 1
|
|
}
|
|
|
|
hash_sha256() {
|
|
target="${1}"
|
|
if is_command sha256sum; then
|
|
hash="$(sha256sum "${target}")" || return 1
|
|
printf '%s' "${hash}" | cut -d ' ' -f 1
|
|
elif is_command shasum; then
|
|
hash="$(shasum -a 256 "${target}" 2>/dev/null)" || return 1
|
|
printf '%s' "${hash}" | cut -d ' ' -f 1
|
|
elif is_command sha256; then
|
|
hash="$(sha256 -q "${target}" 2>/dev/null)" || return 1
|
|
printf '%s' "${hash}" | cut -d ' ' -f 1
|
|
elif is_command openssl; then
|
|
hash="$(openssl dgst -sha256 "${target}")" || return 1
|
|
printf '%s' "${hash}" | cut -d ' ' -f a
|
|
else
|
|
log_crit "hash_sha256 unable to find command to compute SHA256 hash"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
hash_sha256_verify() {
|
|
target="${1}"
|
|
checksums="${2}"
|
|
basename="${target##*/}"
|
|
|
|
want="$(grep "${basename}\$" "${checksums}" 2>/dev/null | tr '\t' ' ' | cut -d ' ' -f 1)"
|
|
if [ -z "${want}" ]; then
|
|
log_err "hash_sha256_verify unable to find checksum for ${target} in ${checksums}"
|
|
return 1
|
|
fi
|
|
|
|
got="$(hash_sha256 "${target}")"
|
|
if [ "${want}" != "${got}" ]; then
|
|
log_err "hash_sha256_verify checksum for ${target} did not verify ${want} vs ${got}"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
untar() {
|
|
tarball="${1}"
|
|
case "${tarball}" in
|
|
*.tar.gz | *.tgz) tar -xzf "${tarball}" ;;
|
|
*.tar) tar -xf "${tarball}" ;;
|
|
*.zip) unzip -- "${tarball}" ;;
|
|
*)
|
|
log_err "untar unknown archive format for ${tarball}"
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
is_command() {
|
|
type "${1}" >/dev/null 2>&1
|
|
}
|
|
|
|
log_debug() {
|
|
[ 3 -le "${LOG_LEVEL}" ] || return 0
|
|
printf 'debug %s\n' "${*}" 1>&2
|
|
}
|
|
|
|
log_info() {
|
|
[ 2 -le "${LOG_LEVEL}" ] || return 0
|
|
printf 'info %s\n' "${*}" 1>&2
|
|
}
|
|
|
|
log_err() {
|
|
[ 1 -le "${LOG_LEVEL}" ] || return 0
|
|
printf 'error %s\n' "${*}" 1>&2
|
|
}
|
|
|
|
log_crit() {
|
|
[ 0 -le "${LOG_LEVEL}" ] || return 0
|
|
printf 'critical %s\n' "${*}" 1>&2
|
|
}
|
|
|
|
main "${@}"
|