Add checksum-verified release installers
This commit is contained in:
parent
47d16eb714
commit
cb8c4e6fd4
9 changed files with 477 additions and 15 deletions
216
scripts/install.sh
Executable file
216
scripts/install.sh
Executable file
|
|
@ -0,0 +1,216 @@
|
|||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
default_release_base="https://forge.tionis.dev/eric/geth/releases/download"
|
||||
version=""
|
||||
install_dir="${GETH_INSTALL_DIR:-${HOME}/.local/bin}"
|
||||
release_base="${GETH_RELEASE_BASE_URL:-$default_release_base}"
|
||||
print_asset=false
|
||||
archive_path=""
|
||||
checksum_path=""
|
||||
modify_path=true
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Install a checksum-verified geth release for the current user.
|
||||
|
||||
Usage:
|
||||
install.sh --version <vX.Y.Z> [--install-dir <dir>] [--release-base-url <url>]
|
||||
install.sh --version <vX.Y.Z> --archive <file> [--checksum <file>]
|
||||
install.sh --version <vX.Y.Z> --print-asset
|
||||
|
||||
The installer only places the single geth executable. It never initializes a
|
||||
geth home or starts a daemon. GETH_INSTALL_DIR and GETH_RELEASE_BASE_URL provide
|
||||
the corresponding option defaults.
|
||||
EOF
|
||||
}
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
--version)
|
||||
[ "$#" -ge 2 ] || { echo "error: --version requires a value" >&2; exit 2; }
|
||||
version=$2
|
||||
shift 2
|
||||
;;
|
||||
--install-dir)
|
||||
[ "$#" -ge 2 ] || { echo "error: --install-dir requires a value" >&2; exit 2; }
|
||||
install_dir=$2
|
||||
shift 2
|
||||
;;
|
||||
--release-base-url)
|
||||
[ "$#" -ge 2 ] || { echo "error: --release-base-url requires a value" >&2; exit 2; }
|
||||
release_base=$2
|
||||
shift 2
|
||||
;;
|
||||
--archive)
|
||||
[ "$#" -ge 2 ] || { echo "error: --archive requires a value" >&2; exit 2; }
|
||||
archive_path=$2
|
||||
shift 2
|
||||
;;
|
||||
--checksum)
|
||||
[ "$#" -ge 2 ] || { echo "error: --checksum requires a value" >&2; exit 2; }
|
||||
checksum_path=$2
|
||||
shift 2
|
||||
;;
|
||||
--no-modify-path)
|
||||
modify_path=false
|
||||
shift
|
||||
;;
|
||||
--print-asset)
|
||||
print_asset=true
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "error: unknown argument: $1" >&2
|
||||
usage >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ -n "$version" ] || { echo "error: --version is required" >&2; usage >&2; exit 2; }
|
||||
if printf '%s' "$install_dir" | LC_ALL=C grep '[[:cntrl:]]' >/dev/null 2>&1; then
|
||||
echo "error: install directory must not contain control characters" >&2
|
||||
exit 2
|
||||
fi
|
||||
case "$version" in
|
||||
v[0-9]*[!0-9A-Za-z._-]*|v[0-9]*/*)
|
||||
echo "error: version contains unsupported characters" >&2
|
||||
exit 2
|
||||
;;
|
||||
v[0-9]*) ;;
|
||||
*) echo "error: version must be an explicit tag such as v0.1.0" >&2; exit 2 ;;
|
||||
esac
|
||||
case "$install_dir" in
|
||||
/*) ;;
|
||||
*) echo "error: install directory must be an absolute path" >&2; exit 2 ;;
|
||||
esac
|
||||
|
||||
os=$(uname -s)
|
||||
arch=$(uname -m)
|
||||
case "$os:$arch" in
|
||||
Linux:x86_64|Linux:amd64)
|
||||
platform=linux-x86_64
|
||||
;;
|
||||
Darwin:x86_64|Darwin:amd64)
|
||||
platform=macos-x86_64
|
||||
;;
|
||||
Darwin:arm64|Darwin:aarch64)
|
||||
platform=macos-aarch64
|
||||
;;
|
||||
*)
|
||||
echo "error: no geth release artifact for $os $arch" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
asset="geth-${version}-${platform}.tar.gz"
|
||||
if [ "$print_asset" = true ]; then
|
||||
printf '%s\n' "$asset"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
[ -n "$archive_path" ] || command -v curl >/dev/null 2>&1 || { echo "error: curl is required" >&2; exit 1; }
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
hash_command=sha256sum
|
||||
elif command -v shasum >/dev/null 2>&1; then
|
||||
hash_command='shasum -a 256'
|
||||
else
|
||||
echo "error: sha256sum or shasum is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tmp_dir=$(mktemp -d "${TMPDIR:-/tmp}/geth-install.XXXXXX")
|
||||
staged_binary=""
|
||||
cleanup() {
|
||||
rm -rf "$tmp_dir"
|
||||
if [ -n "$staged_binary" ] && [ -f "$staged_binary" ]; then
|
||||
rm -f "$staged_binary"
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
if [ -n "$archive_path" ]; then
|
||||
archive=$archive_path
|
||||
checksum=${checksum_path:-$archive_path.sha256}
|
||||
[ -f "$archive" ] || { echo "error: archive not found: $archive" >&2; exit 1; }
|
||||
[ -f "$checksum" ] || { echo "error: checksum not found: $checksum" >&2; exit 1; }
|
||||
echo "using local archive $archive"
|
||||
else
|
||||
[ -z "$checksum_path" ] || { echo "error: --checksum requires --archive" >&2; exit 2; }
|
||||
archive="$tmp_dir/$asset"
|
||||
checksum="$archive.sha256"
|
||||
url="${release_base%/}/${version}/${asset}"
|
||||
echo "downloading $url"
|
||||
curl -fL --proto '=https' --proto-redir '=https' --tlsv1.2 -o "$archive" "$url"
|
||||
curl -fL --proto '=https' --proto-redir '=https' --tlsv1.2 -o "$checksum" "$url.sha256"
|
||||
fi
|
||||
|
||||
expected=$(awk 'NR == 1 { print tolower($1) }' "$checksum")
|
||||
case "$expected" in
|
||||
*[!0-9a-f]*|'') echo "error: invalid SHA-256 file for $asset" >&2; exit 1 ;;
|
||||
esac
|
||||
[ "${#expected}" -eq 64 ] || { echo "error: invalid SHA-256 length for $asset" >&2; exit 1; }
|
||||
if [ "$hash_command" = sha256sum ]; then
|
||||
actual=$(sha256sum "$archive" | awk '{ print tolower($1) }')
|
||||
else
|
||||
actual=$(shasum -a 256 "$archive" | awk '{ print tolower($1) }')
|
||||
fi
|
||||
[ "$actual" = "$expected" ] || { echo "error: checksum mismatch for $asset" >&2; exit 1; }
|
||||
echo "verified SHA-256: $actual"
|
||||
|
||||
if [ -n "$archive_path" ]; then
|
||||
package=$(basename "$archive")
|
||||
package=${package%.tar.gz}
|
||||
else
|
||||
package=${asset%.tar.gz}
|
||||
fi
|
||||
tar -xzf "$archive" -C "$tmp_dir"
|
||||
source_binary="$tmp_dir/$package/geth"
|
||||
[ -f "$source_binary" ] || { echo "error: archive does not contain $package/geth" >&2; exit 1; }
|
||||
|
||||
mkdir -p "$install_dir"
|
||||
staged_binary="$install_dir/.geth.install.$$"
|
||||
cp "$source_binary" "$staged_binary"
|
||||
chmod 755 "$staged_binary"
|
||||
mv -f "$staged_binary" "$install_dir/geth"
|
||||
|
||||
echo "installed geth to $install_dir/geth"
|
||||
case ":${PATH}:" in
|
||||
*":${install_dir}:"*) ;;
|
||||
*)
|
||||
if [ "$modify_path" = true ]; then
|
||||
case "${SHELL:-}" in
|
||||
*/zsh) path_profile=${GETH_PATH_PROFILE:-$HOME/.zprofile} ;;
|
||||
*) path_profile=${GETH_PATH_PROFILE:-$HOME/.profile} ;;
|
||||
esac
|
||||
escaped_install_dir=$(printf '%s' "$install_dir" | sed 's/[\\`"$]/\\&/g')
|
||||
path_marker="# geth installer PATH: $install_dir"
|
||||
path_added=false
|
||||
path_update_failed=false
|
||||
if [ ! -f "$path_profile" ] || ! grep -F "$path_marker" "$path_profile" >/dev/null 2>&1; then
|
||||
if printf '\n%s\nexport PATH="%s:\044PATH"\n' "$path_marker" "$escaped_install_dir" >> "$path_profile"; then
|
||||
path_added=true
|
||||
else
|
||||
path_update_failed=true
|
||||
fi
|
||||
fi
|
||||
if [ "$path_added" = true ]; then
|
||||
echo "added $install_dir to PATH in $path_profile; open a new terminal to use it"
|
||||
elif [ "$path_update_failed" = true ]; then
|
||||
echo "warning: could not update $path_profile; add this directory to PATH:" >&2
|
||||
echo " export PATH=\"$install_dir:\$PATH\"" >&2
|
||||
else
|
||||
echo "$install_dir is already configured in $path_profile; open a new terminal to use it"
|
||||
fi
|
||||
else
|
||||
echo "add this directory to PATH before using geth:"
|
||||
echo " export PATH=\"$install_dir:\$PATH\""
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
echo "the daemon was not started; run 'geth daemon install' when ready"
|
||||
Loading…
Reference in a new issue