mirror of
https://github.com/prdlk/dotfiles.git
synced 2026-11-02 11:32:42 +00:00
feat(public): add chezmoi install script
This commit is contained in:
1 parent
1da988c876
commit
2845895ff4
1 file changed
+395
Executable
+395
@@ -0,0 +1,395 @@
|
||||
#!/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 "${@}"
|
||||
Reference in new issue
Block a user