#!/usr/bin/env bash # Installs the Astro CLI. # # curl -sSL install.astronomer.io | bash -s -- [tag] # into ~/.local/bin # curl -sSL install.astronomer.io | sudo bash -s -- [tag] # into /usr/local/bin # # Without root it installs into $XDG_BIN_HOME, or ~/.local/bin when that is unset. # ASTRO_INSTALL_DIR overrides the directory either way. sudo drops your # environment, so set it on the sudo side of the pipe: # # curl -sSL install.astronomer.io | sudo ASTRO_INSTALL_DIR=/opt/bin bash -s # # Everything runs from main, called on the last line, so a download cut short # runs nothing. set -euo pipefail DOWNLOADER="https://raw.githubusercontent.com/astronomer/astro-cli/main/godownloader.sh" fail() { echo "Error: $*" >&2 exit 1 } # Prints $1 as an absolute path with symlinks resolved, or nothing if it is not # a directory. physical() { if [[ -n "$1" && -d "$1" ]]; then (CDPATH="" cd -P -- "$1" 2>/dev/null && pwd) || true fi } # Succeeds when directory $1 is on PATH, however PATH spells it. on_path() { local want entry entries found=1 want=$(physical "$1") # read splits on the colons without the glob expansion an unquoted $PATH # would get, so an entry with a * in it stays one entry. IFS=: read -r -a entries <<<"$PATH" for entry in ${entries[@]+"${entries[@]}"}; do if [[ -n "$entry" && "$(physical "$entry")" == "$want" ]]; then found=0 break fi done return "$found" } # Prints how to put $1 on PATH for the user's shell. It never edits a profile. path_hint() { echo echo "$1 is not on your PATH." case "$(basename "${SHELL:-}")" in zsh) echo "Add this line to ~/.zshrc, then open a new terminal:" ;; bash) echo "Add this line to ~/.bashrc (~/.bash_profile on macOS), then open a new terminal:" ;; fish) echo "Run this once in fish:" echo " fish_add_path \"$1\"" return ;; *) echo "Add this line to your shell's profile (e.g. ~/.profile), then open a new terminal:" ;; esac echo " export PATH=\"$1:\$PATH\"" } main() { TAG=${1:-} local args=${TAG:+ -- $TAG} local dir=${ASTRO_INSTALL_DIR:-} if [[ -z "$dir" ]]; then if (( EUID == 0 )); then dir=/usr/local/bin elif [[ -n "${XDG_BIN_HOME:-}" ]]; then dir=$XDG_BIN_HOME elif [[ -n "${HOME:-}" ]]; then dir=$HOME/.local/bin else fail "HOME is not set, so there is no ~/.local/bin to install into. Set ASTRO_INSTALL_DIR: curl -sSL install.astronomer.io | ASTRO_INSTALL_DIR=/path/to/bin bash -s$args" fi fi # The directory, or the nearest ancestor that exists, must be writable. # A dangling symlink on the way would make mkdir fail after the download. local existing=$dir while [[ ! -e "$existing" ]]; do if [[ -L "$existing" ]]; then fail "$existing is a symlink to $(readlink "$existing"), which doesn't exist. Create the target, or set ASTRO_INSTALL_DIR: curl -sSL install.astronomer.io | ASTRO_INSTALL_DIR=/path/to/bin bash -s$args" fi existing=$(dirname "$existing") done if [[ ! -d "$existing" || ! -w "$existing" ]]; then if (( EUID == 0 )); then fail "cannot write to $dir. Set ASTRO_INSTALL_DIR to a directory that can be written to." fi fail "cannot write to $dir. Re-run with sudo to install system-wide: curl -sSL install.astronomer.io | sudo bash -s$args or set ASTRO_INSTALL_DIR to a directory you can write to." fi local downloader downloader=$(curl -fsSL "$DOWNLOADER") || fail "could not download $DOWNLOADER" mkdir -p "$dir" if ! bash -s -- -b "$dir" "$TAG" <<<"$downloader"; then # Take back the empty directories this script created, and nothing else. local d=$dir while [[ "$d" != "$existing" ]] && rmdir "$d" 2>/dev/null; do d=$(dirname "$d"); done exit 1 fi # Root's PATH, and sudo's above all, is not the one the user types astro # into, so the PATH hint and the shadow check are for non-root installs. (( EUID != 0 )) || return 0 dir=$(CDPATH="" cd -- "$dir" && pwd) # godownloader installs astro.exe on Windows (Git Bash, MSYS, Cygwin). local bin="$dir/astro" found [[ ! -e "$bin" && -e "$bin.exe" ]] && bin="$bin.exe" found=$(command -v astro || true) if on_path "$dir"; then if [[ -n "$found" && ! "$found" -ef "$bin" ]]; then { echo echo "Warning: another astro comes first on your PATH and shadows the one just installed:" echo " runs: $found" echo " installed: $bin" echo "Remove $found, or put $dir ahead of it in PATH." } >&2 fi else path_hint "$dir" if [[ -n "$found" ]]; then echo echo "Until then, astro runs $found. Put $dir ahead of it to use the one just installed." fi fi } main "$@"