AkurAI Build
Menu

Public repository / 9c0f98ff6f19b1986884b04e14b9fc9e24c11912

AkurAI-EC2 / install.sh

Latest change 9c0f98ff6f19b1986884b04e14b9fc9e24c11912 - Install EC2 CLI over broken symlinks by Ólafur Búi Ólafsson

#!/usr/bin/env bash
set -euo pipefail

usage() {
  cat <<'EOF'
usage: ./install.sh [--symlink|--copy] [--dry-run]

Install bin/akurai-ec2 to ~/.local/bin/akurai-ec2.

  --symlink  install a symlink (default)
  --copy     install a standalone copy
  --dry-run  print the actions without changing anything
EOF
}

die() {
  printf 'install.sh: %s\n' "$*" >&2
  exit 1
}

run() {
  if [ "$dry_run" -eq 1 ]; then
    printf '+'
    printf ' %q' "$@"
    printf '\n'
  else
    "$@"
  fi
}

mode="symlink"
dry_run=0

while [ $# -gt 0 ]; do
  case "$1" in
    --symlink)
      mode="symlink"
      ;;
    --copy)
      mode="copy"
      ;;
    --dry-run)
      dry_run=1
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      die "unknown argument: $1"
      ;;
  esac
  shift
done

script_dir="$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
src="$script_dir/bin/akurai-ec2"
helper_sources=(
  "$script_dir/libexec/akurai-ec2-manifest.py"
  "$script_dir/libexec/akurai-ec2-dns.py"
  "$script_dir/libexec/akurai-ec2-retire-app.py"
)
target="${AKURAI_EC2_INSTALL_TARGET:-$HOME/.local/bin/akurai-ec2}"
target_dir="$(dirname -- "$target")"
helper_target_dir="$(dirname -- "$target_dir")/libexec"
helper_target="$helper_target_dir/akurai-ec2-manifest.py"

[ -f "$src" ] || die "source script not found: $src"
if [ "$mode" = "copy" ]; then
  for helper_src in "${helper_sources[@]}"; do
    [ -f "$helper_src" ] || die "helper not found: $helper_src"
  done
fi

src_resolved="$(readlink -f -- "$src")"
timestamp="$(date +%Y%m%d%H%M%S)"
backup=""

run mkdir -p -- "$target_dir"
run chmod 755 -- "$src"

if [ "$mode" = "symlink" ] && [ -L "$target" ]; then
  target_resolved="$(readlink -f -- "$target" 2>/dev/null || true)"
  if [ "$target_resolved" = "$src_resolved" ]; then
    printf 'already linked: %s -> %s\n' "$target" "$src"
    exit 0
  fi
fi

if [ "$mode" = "copy" ] && [ -f "$target" ] && [ ! -L "$target" ] && cmp -s -- "$src" "$target" \
  && [ -f "$helper_target" ] && cmp -s -- "${helper_sources[0]}" "$helper_target"; then
  run chmod 755 -- "$target"
  run chmod 755 -- "$helper_target"
  printf 'already installed: %s\n' "$target"
  exit 0
fi

if [ -e "$target" ]; then
  backup="$target.bak.$timestamp"
  # Copy through a symlink so every snapshot is a regular, immutable file.
  run install -m 755 -- "$target" "$backup"
  run rm -- "$target"
  printf 'backed up existing target to %s\n' "$backup"
elif [ -L "$target" ]; then
  # Containerized installers may see a host symlink whose source is not mounted.
  run rm -- "$target"
  printf 'removed broken symlink: %s\n' "$target"
fi

if [ "$mode" = "copy" ]; then
  run mkdir -p -- "$helper_target_dir"
  run install -m 755 -- "$src" "$target"
  for helper_src in "${helper_sources[@]}"; do
    helper_target="$helper_target_dir/$(basename -- "$helper_src")"
    run install -m 755 -- "$helper_src" "$helper_target"
    printf 'installed helper: %s\n' "$helper_target"
  done
  printf 'installed copy: %s\n' "$target"
else
  run ln -s -- "$src" "$target"
  printf 'installed symlink: %s -> %s\n' "$target" "$src"
fi

# Keep the five newest regular snapshots; never treat a symlink as a rollback
# artifact even if an older installer left one behind.
backup_candidates=("$target".bak.*)
backups=()
for backup_candidate in "${backup_candidates[@]}"; do
  if [ -f "$backup_candidate" ] && [ ! -L "$backup_candidate" ]; then
    backups+=("$backup_candidate")
  fi
done
while [ "${#backups[@]}" -gt 5 ]; do
  run rm -- "${backups[0]}"
  backups=("${backups[@]:1}")
done