#!/bin/sh # # Installs the terminal version of Edit as the `edit` command. # # curl -fsSL https://rustedit.dev/install.sh | sh # # EDIT_INSTALL_DIR where `edit` goes (default: ~/.local/bin) # EDIT_NO_MODIFY_PATH=1 leave the shell profile alone # # Run it again to update. To remove Edit, delete the `edit` file it names at the end. set -eu SITE="${EDIT_SITE:-https://rustedit.dev}" INSTALL_DIR="${EDIT_INSTALL_DIR:-$HOME/.local/bin}" say() { printf '\033[1;36medit\033[0m %s\n' "$*"; } fail() { printf '\033[1;31medit\033[0m %s\n' "$*" >&2; exit 1; } need() { command -v "$1" >/dev/null 2>&1 || fail "This installer needs $1." } # Reads one field of the terminal build from release.json. plutil ships with macOS and reads JSON. release_field() { index=0 while id="$(plutil -extract "apps.$index.id" raw -o - "$1" 2>/dev/null)"; do if [ "$id" = "terminal" ]; then plutil -extract "apps.$index.$2" raw -o - "$1" return fi index=$((index + 1)) done return 1 } # The Team ID in "Developer ID Application: Name (TEAMID)". team_of_signer() { printf '%s\n' "$1" | sed -n 's/.*(\([A-Z0-9]\{10\}\))$/\1/p' } profile_for_shell() { case "${SHELL:-}" in */zsh) printf '%s\n' "${ZDOTDIR:-$HOME}/.zshrc" ;; */bash) printf '%s\n' "$HOME/.bash_profile" ;; */fish) printf '%s\n' "${XDG_CONFIG_HOME:-$HOME/.config}/fish/conf.d/edit.fish" ;; *) printf '%s\n' "$HOME/.profile" ;; esac } add_to_path() { case ":$PATH:" in *":$INSTALL_DIR:"*) return 0 ;; esac if [ "${EDIT_NO_MODIFY_PATH:-0}" = "1" ]; then say "Add $INSTALL_DIR to your PATH to run edit by name." RUN_WITH="$INSTALL_DIR/edit" return 0 fi profile="$(profile_for_shell)" case "$profile" in *.fish) line="fish_add_path \"$INSTALL_DIR\"" ;; *) line="export PATH=\"$INSTALL_DIR:\$PATH\"" ;; esac if [ -f "$profile" ] && grep -Fqx "$line" "$profile"; then : else mkdir -p "$(dirname "$profile")" printf '\n# Added by the Edit installer\n%s\n' "$line" >> "$profile" say "Added $INSTALL_DIR to PATH in $profile." fi NEW_SHELL_NEEDED=1 } main() { case "$(uname -s)" in Darwin) ;; Linux) fail "There is no Linux build to download yet. This installer covers macOS." ;; *) fail "This installer covers macOS. $(uname -s) is not supported yet." ;; esac case "$(uname -m)" in arm64) ;; *) fail "The macOS build is for Apple silicon. This Mac reports $(uname -m)." ;; esac for tool in curl plutil shasum ditto codesign; do need "$tool" done work="$(mktemp -d "${TMPDIR:-/tmp}/edit-install.XXXXXX")" trap 'rm -rf "$work"' EXIT INT TERM say "Reading the latest release from $SITE ..." curl -fsSL "$SITE/downloads/release.json" -o "$work/release.json" \ || fail "Could not read $SITE/downloads/release.json." file="$(release_field "$work/release.json" file)" || fail "The release lists no terminal build." version="$(release_field "$work/release.json" version)" sha="$(release_field "$work/release.json" sha256)" signer="$(plutil -extract signer raw -o - "$work/release.json")" case "$file" in *[!A-Za-z0-9._-]* | *..* | "") fail "The release names an unexpected file: $file" ;; esac say "Downloading edit $version ..." curl -fSL --progress-bar "$SITE/downloads/$file" -o "$work/$file" \ || fail "Could not download $file." actual="$(shasum -a 256 "$work/$file" | awk '{print $1}')" [ "$actual" = "$sha" ] || fail "The download does not match its SHA-256. Nothing was installed." ditto -x -k "$work/$file" "$work/unpacked" binary="$(find "$work/unpacked" -type f -name edit | head -n 1)" [ -n "$binary" ] || fail "$file does not contain the edit binary." codesign --verify --strict "$binary" 2>/dev/null || fail "The edit binary is not validly signed. Nothing was installed." expected_team="$(team_of_signer "$signer")" actual_team="$(codesign -dv "$binary" 2>&1 | sed -n 's/^TeamIdentifier=//p')" if [ -n "$expected_team" ] && [ "$actual_team" != "$expected_team" ]; then fail "The edit binary is signed by team $actual_team, not $expected_team. Nothing was installed." fi mkdir -p "$INSTALL_DIR" chmod 755 "$binary" cp "$binary" "$INSTALL_DIR/.edit.new" mv -f "$INSTALL_DIR/.edit.new" "$INSTALL_DIR/edit" say "Installed edit $version to $INSTALL_DIR/edit." NEW_SHELL_NEEDED=0 RUN_WITH=edit add_to_path found="$(command -v edit 2>/dev/null || true)" if [ -n "$found" ] && [ "$found" != "$INSTALL_DIR/edit" ]; then say "Note: $found comes first on your PATH, so 'edit' runs that one." fi if [ "$NEW_SHELL_NEEDED" = "1" ]; then say "Open a new terminal, then run: edit" else say "Run: $RUN_WITH" fi } main "$@"