#! /bin/sh

# Eldev --- Elisp development tool.
#
# Copyright (C) 2019-2025 Paul Pogonyshev
#
# Author:   Paul Pogonyshev <pogonyshev@gmail.com>
# Homepage: https://github.com/emacs-eldev/eldev

set -e

if [ -z "$ELDEV_EMACS" ]; then
    if [ -z "$EMACS" ]; then
        ELDEV_EMACS=emacs
    else
        ELDEV_EMACS=$EMACS
    fi
fi

ELDEV_CMD=$0

# For determining whether to enable coloring in automatic mode; Elisp has no `isatty' and
# it doesn't look like we could use `terminal-parameters' etc. in batch mode.  Also could
# try determining background color with "\e]11;?\a", but that doesn't work in a subshell
# and cutting it out of our own output in compatible manner is a nightmare.
ELDEV_TTY=
if test -t 1; then
    ELDEV_TTY=t
fi

export ELDEV_EMACS
export ELDEV_CMD
export ELDEV_TTY

$ELDEV_EMACS --batch --no-site-file --no-site-lisp                                                     \
             --execute '(let ((eldev--emacs-version (format "%s.%s" emacs-major-version emacs-minor-version))
      (eldev--dir           (getenv "ELDEV_DIR"))
      ;; This is intentional.  First, this is in case ELDEV_LOCAL is
      ;; defined, second, this is just Eldev default for packages.
      (load-prefer-newer    t))
  ;; Setting `debug-on-error'\'' would be useful, but it can break many
  ;; `package-*'\'' functions, since those use `with-demoted-errors'\'' and
  ;; so `condition-case-unless-debug'\''.
  (unless (and (fboundp '\''version<=) (version<= "24.4" eldev--emacs-version))
    (error "Eldev requires Emacs 24.4 or newer"))
  (setf package-user-dir
        (expand-file-name "bootstrap"
                          (expand-file-name eldev--emacs-version
                                            (if (> (length eldev--dir) 0)
                                                eldev--dir
                                              (if (file-directory-p "~/.eldev")
                                                  "~/.eldev"
                                                ;; Duplicating not-yet-available code from `eldev-xdg-cache-home'\''.
                                                (expand-file-name "eldev"
                                                                  (let ((eldev--xdg-cache-dir (getenv "XDG_CACHE_HOME")))
                                                                    (if (and eldev--xdg-cache-dir (file-name-absolute-p eldev--xdg-cache-dir))
                                                                        eldev--xdg-cache-dir
                                                                      "~/.cache")))))))
        package-directory-list nil
        package-archives       nil)
  (require '\''package)
  (package-initialize t)
  (let ((package-archives '\''(("melpa-stable" . "http://stable.melpa.org/packages/")))
        (archive-name      "MELPA Stable")
        (inhibit-message  t)
        (eldev-local      (getenv "ELDEV_LOCAL"))
        eldev-pkg
        requirements)
    (unless (= (length eldev-local) 0)
      (if (string-prefix-p ":pa:" eldev-local)
          (setf package-archives `(("bootstrap-pa" . ,(file-name-as-directory (substring eldev-local (length ":pa:")))))
                archive-name     "a local package archive")
        (with-temp-buffer
          (insert-file-contents (expand-file-name "eldev.el" eldev-local))
          (setf eldev-pkg                    (package-buffer-info)
                (package-desc-dir eldev-pkg) (expand-file-name eldev-local))
          ;; Currently Eldev has no external dependencies, but let'\''s be generic.
          (dolist (requirement (package-desc-reqs eldev-pkg))
            (unless (package-activate (car requirement))
              (push requirement requirements))))))
    (when (if eldev-pkg
              requirements
            (not (package-activate '\''eldev)))
      (let ((inhibit-message nil))
        (message "Bootstrapping Eldev for Emacs %s from %s...\n" eldev--emacs-version archive-name)
        (when eldev-pkg
          (message "Eldev package itself will be used from `%s'\''\n" eldev-local)))
      ;; See `eldev-retrying-for-robustness'\''; since Eldev is not bootstrapped yet, we have
      ;; to inline everything.  No control from command line here.
      (let* ((all-retry-delays (when (equal (getenv "CI") "true") '\''(30 60 120 180 300)))
             (remaining-delays all-retry-delays))
        (catch '\''obtained-result
          (while t
            (condition-case error
                (throw '\''obtained-result (let ((debug-on-error (and debug-on-error (null remaining-delays))))
                                          ;; See similar workarounds for `package-refresh-contents'\'' in `eldev.el'\''.
                                          (let* (failure
                                                 (failure-catcher (lambda (original archive &rest arguments)
                                                                    (unless failure
                                                                      (condition-case-unless-debug error
                                                                          (apply original archive arguments)
                                                                        (error (setf failure (cons error (if (consp archive) (car archive) archive)))))))))
                                            (advice-add '\''package--download-one-archive :around failure-catcher)
                                            (unwind-protect
                                                (package-refresh-contents)
                                              (advice-remove '\''package--download-one-archive failure-catcher))
                                            (when failure
                                              (error "%s (when updating contents of package archive `%s'\'')" (error-message-string (car failure)) (cdr failure))))))
              (error (let ((inhibit-message nil)
                           (delay           (pop remaining-delays)))
                       (unless delay
                         (when all-retry-delays
                           (message "Giving up: too many retries already"))
                         (signal (car error) (cdr error)))
                       (message "%s" (error-message-string error))
                       (message "Assuming this is an intermittent problem, waiting %s before retrying...\n"
                                (if (< delay 60) (format "%s s" delay) (format "%s m" (/ delay 60))))
                       (sleep-for delay)
                       (let ((n (- 5 (length remaining-delays))))
                         (message "Retry #%d%s..." n (if (= n 5) ", the last" " of maximum 5")))))))))
      (if eldev-pkg
          (package-download-transaction (package-compute-transaction nil requirements))
        (package-install '\''eldev)))
    (when eldev-pkg
      (push `(eldev . (,eldev-pkg)) package-alist)
      ;; `package--autoloads-file-name'\'' is package-private.
      (let* ((autoloads-file     (expand-file-name (format "%s-autoloads" (package-desc-name eldev-pkg))
                                                   (package-desc-dir eldev-pkg)))
             (autoloads-disabler (lambda (do-load file &rest args) (unless (equal file autoloads-file) (apply do-load file args)))))
        ;; Otherwise old Emacs versions print an ugly error having not found the autoloads file.
        (advice-add #'\''load :around autoloads-disabler)
        (package-activate-1 eldev-pkg)
        ;; As of commit 1d5b164109b in Emacs repository, `package-activate-1'\'' no longer modifies `load-path'\'',
        ;; leaving this to the autoloads file.  As we don'\''t have such a file, we have to do that ourselves.
        (add-to-list '\''load-path (package-desc-dir eldev-pkg))
        (advice-remove #'\''load autoloads-disabler))))
  (require '\''eldev)
  (eldev-start-up))'   \
             --execute "(kill-emacs (eldev-cli (append (cdr (member \"--\" command-line-args)) nil)))" \
             -- "$@"
