From b9376919aa644b9572997042b175bb5a3be2aa70 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sat, 3 Jun 2023 23:54:03 +0200 Subject: [PATCH] feat: Add get.chezmoi.io/lb and chezmoi.io/getlb install scripts --- .github/workflows/main.yml | 2 + assets/chezmoi.io/docs/hooks.py | 1 + assets/chezmoi.io/docs/install.md.tmpl | 10 +- assets/scripts/install-local-bin.sh | 357 ++++++++++++++++++ .../cmds/generate-install.sh/install.sh.tmpl | 2 +- internal/cmds/generate-install.sh/main.go | 7 +- main.go | 1 + 7 files changed, 376 insertions(+), 4 deletions(-) create mode 100644 assets/scripts/install-local-bin.sh diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 36fda0cac69..cbbc812abb3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -439,6 +439,7 @@ jobs: - name: prepare-get.chezmoi.io run: | cp assets/scripts/install.sh assets/get.chezmoi.io/index.html + cp assets/scripts/install-local-bin.sh assets/get.chezmoi.io/lb cp assets/scripts/install.ps1 assets/get.chezmoi.io/ps1 cp LICENSE assets/get.chezmoi.io/LICENSE - name: push-get.chezmoi.io @@ -451,3 +452,4 @@ jobs: destination-repository-name: get.chezmoi.io target-branch: gh-pages commit-message: 'chore: Update from ORIGIN_COMMIT' + user-email: twpayne@gmail.com diff --git a/assets/chezmoi.io/docs/hooks.py b/assets/chezmoi.io/docs/hooks.py index 1210081bcc3..c5067f7b6ba 100644 --- a/assets/chezmoi.io/docs/hooks.py +++ b/assets/chezmoi.io/docs/hooks.py @@ -56,6 +56,7 @@ def on_post_build(config, **kwargs): # copy installation scripts utils.copy_file('../scripts/install.sh', os.path.join(site_dir, 'get')) + utils.copy_file('../scripts/install-local-bin.sh', os.path.join(site_dir, 'getlb')) utils.copy_file('../scripts/install.ps1', os.path.join(site_dir, 'get.ps1')) # copy cosign.pub diff --git a/assets/chezmoi.io/docs/install.md.tmpl b/assets/chezmoi.io/docs/install.md.tmpl index 6e2eaf310e6..c141073a3d8 100644 --- a/assets/chezmoi.io/docs/install.md.tmpl +++ b/assets/chezmoi.io/docs/install.md.tmpl @@ -168,6 +168,11 @@ with a single command: sh -c "$(curl -fsLS get.chezmoi.io)" -- init --apply git@github.com:$GITHUB_USERNAME/dotfiles.git ``` +!!! hint + + If you want to install chezmoi in `./.local/bin` instead of `./bin` you can + use `get.chezmoi.io/lb` or `chezmoi.io/getlb` instead. + !!! hint To install the chezmoi binary in a different directory, use the `-b` option, @@ -295,8 +300,9 @@ Verify the signature of the checksum file with cosign. cosign should print `Verified OK` -Verify the that the SHA256 sum of your downloads match the SHA256 sum in the -verified checksum file. All your downloads must be in the current directory. +Verify the that the SHA256 sum of your downloads matches the SHA256 sum in the +verified checksum file. All the downloaded files must be in the current +directory. === "Linux" diff --git a/assets/scripts/install-local-bin.sh b/assets/scripts/install-local-bin.sh new file mode 100644 index 00000000000..c39341eafcb --- /dev/null +++ b/assets/scripts/install-local-bin.sh @@ -0,0 +1,357 @@ +#!/bin/sh + +# chezmoi install script +# contains code from and inspired by +# https://github.com/client9/shlib +# https://github.com/goreleaser/godownloader + +set -e + +BINDIR="${BINDIR:-.local/bin}" +CHEZMOI_USER_REPO="${CHEZMOI_USER_REPO:-twpayne/chezmoi}" +TAGARG=latest +LOG_LEVEL=2 + +GITHUB_DOWNLOAD="https://github.com/${CHEZMOI_USER_REPO}/releases/download" + +tmpdir="$(mktemp -d)" +trap 'rm -rf -- "${tmpdir}"' EXIT +trap 'exit' INT TERM + +usage() { + this="${1}" + cat <&2 + return 1 + ;; + esac +} + +get_libc() { + if is_command ldd; then + case "$(ldd --version 2>&1 | tr '[:upper:]' '[:lower:]')" in + *glibc* | *"gnu libc"*) + 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/${CHEZMOI_USER_REPO}/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}' -sL -o "${local_file}" "${source_url}")" + else + code="$(curl -w '%{http_code}' -sL -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 "${@}" diff --git a/internal/cmds/generate-install.sh/install.sh.tmpl b/internal/cmds/generate-install.sh/install.sh.tmpl index 17c5e9c61f6..21fd08f470b 100644 --- a/internal/cmds/generate-install.sh/install.sh.tmpl +++ b/internal/cmds/generate-install.sh/install.sh.tmpl @@ -7,7 +7,7 @@ set -e -BINDIR="${BINDIR:-./bin}" +BINDIR="${BINDIR:-{{ .BinDir }}}" CHEZMOI_USER_REPO="${CHEZMOI_USER_REPO:-twpayne/chezmoi}" TAGARG=latest LOG_LEVEL=2 diff --git a/internal/cmds/generate-install.sh/main.go b/internal/cmds/generate-install.sh/main.go index 07b21b8462d..aa2e4bf6326 100644 --- a/internal/cmds/generate-install.sh/main.go +++ b/internal/cmds/generate-install.sh/main.go @@ -12,7 +12,10 @@ import ( "gopkg.in/yaml.v3" ) -var output = flag.String("o", "", "output") +var ( + binDir = flag.String("b", "./bin", "binary directory") + output = flag.String("o", "", "output") +) type platform struct { GOOS string @@ -128,8 +131,10 @@ func run() error { defer outputFile.Close() } return installShTemplate.ExecuteTemplate(outputFile, "install.sh.tmpl", struct { + BinDir string Platforms []platform }{ + BinDir: *binDir, Platforms: sortedPlatforms, }) } diff --git a/main.go b/main.go index b6c3fb7f428..b82aeeaaece 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ //go:generate go run . completion powershell -o completions/chezmoi.ps1 //go:generate go run . completion zsh -o completions/chezmoi.zsh //go:generate go run ./internal/cmds/generate-install.sh -o assets/scripts/install.sh +//go:generate go run ./internal/cmds/generate-install.sh -b .local/bin -o assets/scripts/install-local-bin.sh package main