diff options
| author | cashmere <cashmere@autistici.org> | 2026-07-28 22:17:22 +0200 |
|---|---|---|
| committer | cashmere <cashmere@autistici.org> | 2026-07-28 22:17:22 +0200 |
| commit | 3a47d15071ca945add6098808983fea9e042b304 (patch) | |
| tree | 8b168c4af6ecc1d99d9b6cfd6c86c5b304447194 /tramp-rpc/lisp/tramp-rpc-deploy.el | |
| parent | 4078dd92977bfd32eca9389ed350393d34175330 (diff) | |
| download | emacs-3a47d15071ca945add6098808983fea9e042b304.tar.gz | |
remove tramp-rpc
Diffstat (limited to 'tramp-rpc/lisp/tramp-rpc-deploy.el')
| -rw-r--r-- | tramp-rpc/lisp/tramp-rpc-deploy.el | 774 |
1 files changed, 0 insertions, 774 deletions
diff --git a/tramp-rpc/lisp/tramp-rpc-deploy.el b/tramp-rpc/lisp/tramp-rpc-deploy.el deleted file mode 100644 index 3cfcb2d..0000000 --- a/tramp-rpc/lisp/tramp-rpc-deploy.el +++ /dev/null @@ -1,774 +0,0 @@ -;;; tramp-rpc-deploy.el --- Binary deployment for TRAMP-RPC -*- lexical-binding: t; -*- - -;; Copyright (C) 2026 Arthur Heymans <arthur@aheymans.xyz> - -;; Author: Arthur Heymans <arthur@aheymans.xyz> -;; Keywords: comm, processes -;; Package-Requires: ((emacs "30.1")) - -;; This file is part of tramp-rpc. - -;;; Commentary: - -;; This file handles deployment of the tramp-rpc-server binary to -;; remote hosts. It supports: -;; - Automatic detection of remote architecture -;; - Downloading pre-compiled binaries from GitHub releases -;; - Building from source as fallback (requires Rust) -;; - Local caching of binaries -;; - Transfer to remote hosts with checksum verification - -;;; Code: - -(require 'tramp) -(require 'url) - -;; Silence byte-compiler warnings for functions defined in tramp-sh -(declare-function tramp-send-command "tramp-sh") -(declare-function tramp-send-command-and-check "tramp-sh") -(declare-function tramp-send-command-and-read "tramp-sh") - -;;; ============================================================================ -;;; Customization -;;; ============================================================================ - -(defgroup tramp-rpc-deploy nil - "Deployment settings for TRAMP-RPC." - :group 'tramp) - -(defconst tramp-rpc-deploy-version "0.3.0" - "Current version of tramp-rpc-server.") - -(defconst tramp-rpc-deploy-binary-name "tramp-rpc-server" - "Name of the server binary.") - -(defcustom tramp-rpc-deploy-github-repo "ArthurHeymans/emacs-tramp-rpc" - "GitHub repository for downloading pre-compiled binaries. -Format: \"owner/repo\"." - :type 'string - :group 'tramp-rpc-deploy) - -(defcustom tramp-rpc-deploy-release-url-format - "https://github.com/%s/releases/download/v%s/%s" - "URL format for downloading release assets. -Arguments: repo, version, filename." - :type 'string - :group 'tramp-rpc-deploy) - -(defcustom tramp-rpc-deploy-local-cache-directory - (expand-file-name "tramp-rpc" user-emacs-directory) - "Local directory for caching downloaded/built binaries. -Binaries are stored as CACHE-DIR/VERSION/ARCH/tramp-rpc-server." - :type 'directory - :group 'tramp-rpc-deploy) - -(defcustom tramp-rpc-deploy-source-directory - (when load-file-name - (expand-file-name ".." (file-name-directory load-file-name))) - "Directory containing the tramp-rpc source code. -Used for building from source. Set to nil to disable source builds." - :type '(choice directory (const nil)) - :group 'tramp-rpc-deploy) - -(defconst tramp-rpc-deploy-bundled-binary-directory - (when load-file-name - (expand-file-name "binaries" (file-name-directory load-file-name))) - "Directory containing pre-built binaries bundled with the package. -This is useful for development - binaries built by scripts/build-all.sh -are placed here and used directly without needing to download or cache.") - -(defcustom tramp-rpc-deploy-remote-directory "~/.cache/tramp-rpc" - "Remote directory where the server binary will be installed." - :type 'string - :group 'tramp-rpc-deploy) - -(defcustom tramp-rpc-deploy-auto-deploy t - "If non-nil, automatically deploy the server binary when needed." - :type 'boolean - :group 'tramp-rpc-deploy) - -(defcustom tramp-rpc-deploy-prefer-build nil - "If non-nil, prefer building from source over downloading. -By default, downloading is attempted first as it's faster." - :type 'boolean - :group 'tramp-rpc-deploy) - -(defcustom tramp-rpc-deploy-bootstrap-method "sshx" - "TRAMP method to use for bootstrapping (deploying the binary). -Use \"sshx\" to avoid PTY allocation issues, or \"ssh\" for standard SSH." - :type 'string - :group 'tramp-rpc-deploy) - -(defcustom tramp-rpc-deploy-max-retries 3 - "Maximum number of retries for binary transfer." - :type 'integer - :group 'tramp-rpc-deploy) - -(defcustom tramp-rpc-deploy-download-timeout 120 - "Timeout in seconds for downloading binaries." - :type 'integer - :group 'tramp-rpc-deploy) - -(defcustom tramp-rpc-deploy-debug nil - "When non-nil, log verbose debug messages during deployment. -Messages are logged to *tramp-rpc-deploy* buffer." - :type 'boolean - :group 'tramp-rpc-deploy) - -(defun tramp-rpc-deploy--log (format-string &rest args) - "Log a debug message if `tramp-rpc-deploy-debug' is non-nil. -FORMAT-STRING and ARGS are passed to `format'." - (when tramp-rpc-deploy-debug - (with-current-buffer (get-buffer-create "*tramp-rpc-deploy*") - (goto-char (point-max)) - (insert (format-time-string "[%Y-%m-%d %H:%M:%S] ") - (apply #'format format-string args) - "\n")))) - -;;; ============================================================================ -;;; Architecture detection and path helpers -;;; ============================================================================ - -(defun tramp-rpc-deploy--bootstrap-vec (vec) - "Convert VEC to use the bootstrap method for deployment operations. -This allows us to use sshx for deployment even when the main method is rpc." - (let ((method (tramp-file-name-method vec))) - (if (member method '("ssh" "sshx" "scpx")) - vec ; Already a shell-based method - ;; Convert to bootstrap method - create a new tramp-file-name struct - (make-tramp-file-name - :method tramp-rpc-deploy-bootstrap-method - :user (tramp-file-name-user vec) - :domain (tramp-file-name-domain vec) - :host (tramp-file-name-host vec) - :port (tramp-file-name-port vec) - :localname (tramp-file-name-localname vec) - :hop (tramp-file-name-hop vec))))) - -(defun tramp-rpc-deploy--detect-remote-arch (vec) - "Detect the architecture of remote host specified by VEC. -Returns a string like \"x86_64-linux\" or \"aarch64-darwin\"." - (let* ((uname-m (string-trim - (tramp-send-command-and-read - vec "echo \\\"`uname -m`\\\""))) - (uname-s (string-trim - (tramp-send-command-and-read - vec "echo \\\"`uname -s`\\\""))) - (arch (pcase uname-m - ("x86_64" "x86_64") - ("amd64" "x86_64") - ("aarch64" "aarch64") - ("arm64" "aarch64") - (_ uname-m))) - (os (pcase (downcase uname-s) - ("linux" "linux") - ("darwin" "darwin") - (_ (downcase uname-s))))) - (format "%s-%s" arch os))) - -(defun tramp-rpc-deploy--detect-local-arch () - "Detect the architecture of the local system. -Returns a string like \"x86_64-linux\" or \"aarch64-darwin\"." - (let* ((arch (pcase system-type - ('gnu/linux "linux") - ('darwin "darwin") - (_ (symbol-name system-type)))) - (machine (car (split-string system-configuration "-"))) - (normalized-machine (pcase machine - ("x86_64" "x86_64") - ("aarch64" "aarch64") - ("arm64" "aarch64") - (_ machine)))) - (format "%s-%s" normalized-machine arch))) - -(defun tramp-rpc-deploy--arch-to-rust-target (arch) - "Convert ARCH string to Rust target triple. -E.g., \"x86_64-linux\" -> \"x86_64-unknown-linux-musl\". -Linux targets use musl for fully static binaries." - (pcase arch - ("x86_64-linux" "x86_64-unknown-linux-musl") - ("aarch64-linux" "aarch64-unknown-linux-musl") - ("x86_64-darwin" "x86_64-apple-darwin") - ("aarch64-darwin" "aarch64-apple-darwin") - (_ (error "Unknown architecture: %s" arch)))) - -(defun tramp-rpc-deploy--local-cache-path (arch) - "Return the local cache path for binary of ARCH." - (expand-file-name - tramp-rpc-deploy-binary-name - (expand-file-name - arch - (expand-file-name - tramp-rpc-deploy-version - tramp-rpc-deploy-local-cache-directory)))) - -(defun tramp-rpc-deploy--bundled-binary-path (arch) - "Return the path to a bundled binary for ARCH, or nil if not available. -Bundled binaries are in lisp/binaries/<arch>/tramp-rpc-server. -This is useful for development - run scripts/build-all.sh to populate." - (when tramp-rpc-deploy-bundled-binary-directory - (let ((path (expand-file-name - tramp-rpc-deploy-binary-name - (expand-file-name arch tramp-rpc-deploy-bundled-binary-directory)))) - (when (and (file-exists-p path) (file-executable-p path)) - path)))) - -(defun tramp-rpc-deploy--remote-binary-path (vec) - "Return the remote path where the binary should be installed for VEC." - (tramp-make-tramp-file-name - vec - ;; Use concat instead of expand-file-name to preserve ~ for remote expansion. - ;; expand-file-name would expand ~ to the LOCAL user's home directory, - ;; causing failures when local and remote usernames differ. - (concat (file-name-as-directory tramp-rpc-deploy-remote-directory) - (format "%s-%s" tramp-rpc-deploy-binary-name tramp-rpc-deploy-version)))) - -;;; ============================================================================ -;;; Download from GitHub Releases -;;; ============================================================================ - -(defun tramp-rpc-deploy--release-asset-name (arch) - "Return the release asset filename for ARCH." - (format "tramp-rpc-server-%s-%s.tar.gz" - (tramp-rpc-deploy--arch-to-rust-target arch) - tramp-rpc-deploy-version)) - -(defun tramp-rpc-deploy--download-url (arch) - "Return the download URL for binary of ARCH." - (format tramp-rpc-deploy-release-url-format - tramp-rpc-deploy-github-repo - tramp-rpc-deploy-version - (tramp-rpc-deploy--release-asset-name arch))) - -(defun tramp-rpc-deploy--checksum-url (arch) - "Return the checksum file URL for binary of ARCH." - (format tramp-rpc-deploy-release-url-format - tramp-rpc-deploy-github-repo - tramp-rpc-deploy-version - (format "tramp-rpc-server-%s-%s.tar.gz.sha256" - (tramp-rpc-deploy--arch-to-rust-target arch) - tramp-rpc-deploy-version))) - -(defun tramp-rpc-deploy--download-file (url dest) - "Download URL to DEST synchronously. -Returns t on success, nil on failure." - (condition-case err - (let ((url-request-method "GET") - (url-show-status nil)) - (message "Downloading %s..." url) - (with-timeout (tramp-rpc-deploy-download-timeout - (error "Download timed out after %d seconds" - tramp-rpc-deploy-download-timeout)) - (with-current-buffer (url-retrieve-synchronously url t t) - (goto-char (point-min)) - ;; Check for HTTP errors - (unless (re-search-forward "^HTTP/[0-9.]+ 200" nil t) - (if (re-search-forward "^HTTP/[0-9.]+ \\([0-9]+\\)" nil t) - (error "HTTP error %s" (match-string 1)) - (error "Invalid HTTP response"))) - ;; Find body (after blank line) - (re-search-forward "^\r?\n" nil t) - ;; Write body to file - (let ((coding-system-for-write 'binary)) - (write-region (point) (point-max) dest nil 'silent)) - (kill-buffer) - t))) - (error - (message "Download failed: %s" (error-message-string err)) - nil))) - -(defun tramp-rpc-deploy--verify-checksum (file expected-checksum) - "Verify that FILE has EXPECTED-CHECKSUM. -Returns t if checksum matches, nil otherwise." - (when (and file (file-exists-p file) expected-checksum) - (let ((actual (with-temp-buffer - (set-buffer-multibyte nil) - (insert-file-contents-literally file) - (secure-hash 'sha256 (current-buffer))))) - (string= actual (car (split-string expected-checksum)))))) - -(defun tramp-rpc-deploy--extract-tarball (tarball dest-dir) - "Extract TARBALL to DEST-DIR. -Returns the path to the extracted binary, or nil on failure." - (let ((default-directory dest-dir)) - (make-directory dest-dir t) - (if (zerop (call-process "tar" nil nil nil "-xzf" tarball "-C" dest-dir)) - (let ((binary (expand-file-name tramp-rpc-deploy-binary-name dest-dir))) - (when (file-exists-p binary) - (set-file-modes binary #o755) - binary)) - nil))) - -(defun tramp-rpc-deploy--download-binary (arch) - "Download pre-compiled binary for ARCH from GitHub releases. -Returns the path to the binary on success, signals error on failure." - (let* ((cache-path (tramp-rpc-deploy--local-cache-path arch)) - (cache-dir (file-name-directory cache-path)) - (tarball-url (tramp-rpc-deploy--download-url arch)) - (checksum-url (tramp-rpc-deploy--checksum-url arch)) - (temp-dir (make-temp-file "tramp-rpc-" t)) - (tarball-path (expand-file-name "server.tar.gz" temp-dir)) - (checksum-path (expand-file-name "server.tar.gz.sha256" temp-dir))) - (unwind-protect - (progn - ;; Download checksum first - (message "Fetching checksum for %s..." arch) - (let ((checksum-ok (tramp-rpc-deploy--download-file checksum-url checksum-path))) - ;; Download tarball - (message "Downloading tramp-rpc-server for %s..." arch) - (unless (tramp-rpc-deploy--download-file tarball-url tarball-path) - (error "Download failed from %s (release may not exist)" tarball-url)) - ;; Verify checksum if we got one - (when checksum-ok - (let ((expected (with-temp-buffer - (insert-file-contents checksum-path) - (buffer-string)))) - (unless (tramp-rpc-deploy--verify-checksum tarball-path expected) - (error "Checksum verification failed")))) - ;; Extract - (message "Extracting binary...") - (make-directory cache-dir t) - (unless (tramp-rpc-deploy--extract-tarball tarball-path cache-dir) - (error "Failed to extract tarball")) - (message "Downloaded tramp-rpc-server for %s" arch) - cache-path)) - ;; Cleanup temp dir - (delete-directory temp-dir t)))) - -;;; ============================================================================ -;;; Build from source -;;; ============================================================================ - -(defun tramp-rpc-deploy--cargo-available-p () - "Check if cargo (Rust) is available." - (executable-find "cargo")) - -(defun tramp-rpc-deploy--can-build-for-arch-p (arch) - "Check if we can build for ARCH on this system. -Cross-compilation requires additional setup, so we only build natively." - (string= arch (tramp-rpc-deploy--detect-local-arch))) - -(defun tramp-rpc-deploy--build-binary (arch) - "Build the binary for ARCH from source. -Returns the path to the binary on success, nil on failure." - (unless tramp-rpc-deploy-source-directory - (error "Source directory not configured")) - (unless (tramp-rpc-deploy--cargo-available-p) - (error "Rust toolchain (cargo) not found")) - (unless (tramp-rpc-deploy--can-build-for-arch-p arch) - (error "Cannot cross-compile for %s on %s" - arch (tramp-rpc-deploy--detect-local-arch))) - - (let* ((default-directory tramp-rpc-deploy-source-directory) - (target (tramp-rpc-deploy--arch-to-rust-target arch)) - (cache-path (tramp-rpc-deploy--local-cache-path arch)) - (cache-dir (file-name-directory cache-path)) - (build-output (expand-file-name - (format "target/%s/release/%s" - target tramp-rpc-deploy-binary-name) - tramp-rpc-deploy-source-directory)) - (build-buffer (get-buffer-create "*tramp-rpc-build*"))) - - (message "Building tramp-rpc-server for %s (this may take a minute)..." arch) - - (with-current-buffer build-buffer - (erase-buffer)) - - (let ((exit-code - (call-process "cargo" nil build-buffer nil - "build" "--release" - "--target" target - "--manifest-path" - (expand-file-name "Cargo.toml" tramp-rpc-deploy-source-directory)))) - (if (zerop exit-code) - (progn - ;; Copy to cache - (make-directory cache-dir t) - (copy-file build-output cache-path t) - (set-file-modes cache-path #o755) - (message "Built tramp-rpc-server for %s" arch) - cache-path) - (with-current-buffer build-buffer - (error "Build failed (exit %d):\n%s" exit-code (buffer-string))))))) - -;;; ============================================================================ -;;; Main logic: ensure local binary exists -;;; ============================================================================ - -(defun tramp-rpc-deploy--ensure-local-binary (arch) - "Ensure a local binary exists for ARCH. -Tries in order: -1. Check bundled binaries (useful for development) -2. Check local cache -3. Download from GitHub releases -4. Build from source (if on same architecture) - -Returns the path to the local binary." - (let ((bundled-path (tramp-rpc-deploy--bundled-binary-path arch)) - (cache-path (tramp-rpc-deploy--local-cache-path arch))) - (cond - ;; Check bundled binaries first (useful for development - run - ;; scripts/build-all.sh to populate lisp/binaries/) - (bundled-path - (message "Using bundled binary for %s" arch) - bundled-path) - - ;; Check cache - ((and (file-exists-p cache-path) - (file-executable-p cache-path)) - (message "Using cached binary for %s" arch) - cache-path) - - ;; Need to obtain binary - (t - (let ((methods (if tramp-rpc-deploy-prefer-build - '(build download) - '(download build))) - (result nil) - (errors nil)) - - (dolist (method methods) - (unless result - (condition-case err - (setq result - (pcase method - ('download - (tramp-rpc-deploy--download-binary arch)) - ('build - (when (and tramp-rpc-deploy-source-directory - (tramp-rpc-deploy--cargo-available-p) - (tramp-rpc-deploy--can-build-for-arch-p arch)) - (tramp-rpc-deploy--build-binary arch))))) - (error - (push (cons method (error-message-string err)) errors))))) - - (or result - (error "Failed to obtain tramp-rpc-server for %s.\n\nErrors:\n%s\n\n%s" - arch - (mapconcat (lambda (e) - (format " %s: %s" (car e) (cdr e))) - (reverse errors) - "\n") - (tramp-rpc-deploy--help-message arch)))))))) - -(defun tramp-rpc-deploy--help-message (arch) - "Return a help message for obtaining binary for ARCH." - (let ((local-arch (tramp-rpc-deploy--detect-local-arch))) - (concat - "To resolve this, you can:\n\n" - (format "1. Download manually from:\n %s\n\n" - (tramp-rpc-deploy--download-url arch)) - (if (string= arch local-arch) - (concat - "2. Install Rust and build from source:\n" - " curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh\n" - " Then restart Emacs and try again.\n\n") - (format - "2. Build on a %s machine and copy to:\n %s\n\n" - arch - (tramp-rpc-deploy--local-cache-path arch))) - (format "Binary should be placed at:\n %s" - (tramp-rpc-deploy--local-cache-path arch))))) - -;;; ============================================================================ -;;; Remote deployment -;;; ============================================================================ - -(defun tramp-rpc-deploy--remote-binary-exists-p (vec) - "Check if the correct version of the binary exists on remote VEC." - (let ((remote-path (tramp-rpc-deploy--remote-binary-path vec))) - ;; Use tramp-sh operations for checking since we're bootstrapping - (tramp-send-command-and-check - vec - (format "test -x %s" - (tramp-shell-quote-argument - (tramp-file-local-name remote-path)))))) - -(defun tramp-rpc-deploy--ensure-remote-directory (vec) - "Ensure the remote deployment directory exists on VEC." - (let ((dir (tramp-file-local-name - (tramp-make-tramp-file-name vec tramp-rpc-deploy-remote-directory)))) - (tramp-send-command vec (format "mkdir -p %s" (tramp-shell-quote-argument dir))))) - -(defun tramp-rpc-deploy--compute-checksum (file) - "Compute SHA256 checksum of local FILE." - (with-temp-buffer - (set-buffer-multibyte nil) - (insert-file-contents-literally file) - (secure-hash 'sha256 (current-buffer)))) - -(defun tramp-rpc-deploy--remote-checksum (vec path) - "Get SHA256 checksum of remote PATH on VEC. -Tries sha256sum first, then shasum -a 256 for macOS compatibility." - ;; Try sha256sum first (Linux), then shasum -a 256 (macOS) - (tramp-send-command vec - (format "{ sha256sum %s 2>/dev/null || shasum -a 256 %s 2>/dev/null; } | cut -d' ' -f1" - (tramp-shell-quote-argument path) - (tramp-shell-quote-argument path))) - (with-current-buffer (tramp-get-connection-buffer vec) - (goto-char (point-min)) - ;; Match exactly 64 hex chars to avoid false positives from error messages - (when (looking-at "\\([a-f0-9]\\{64\\}\\)") - (match-string 1)))) - -(defun tramp-rpc-deploy--transfer-binary (vec local-path) - "Transfer the binary at LOCAL-PATH to the remote host VEC. -Uses TRAMP's copy-file for reliable binary transfer with checksum verification." - (let* ((remote-path (tramp-rpc-deploy--remote-binary-path vec)) - (remote-local (tramp-file-local-name remote-path)) - (remote-tmp-name (format "%s.tmp.%d" - (file-name-nondirectory remote-local) - (random 100000))) - (remote-tmp-path (tramp-make-tramp-file-name - vec - ;; Use concat to preserve ~ for remote expansion - (concat (file-name-as-directory tramp-rpc-deploy-remote-directory) - remote-tmp-name))) - (remote-tmp-local (tramp-file-local-name remote-tmp-path)) - (local-checksum (tramp-rpc-deploy--compute-checksum local-path)) - (retries 0) - (success nil) - (errors nil)) - - (tramp-rpc-deploy--log "Transfer starting: local=%s remote=%s" local-path remote-local) - (tramp-rpc-deploy--log "Local binary size: %d bytes, checksum: %s..." - (file-attribute-size (file-attributes local-path)) - (substring local-checksum 0 16)) - - ;; Ensure remote directory exists - (tramp-rpc-deploy--ensure-remote-directory vec) - - (message "Transferring binary to %s:%s..." (tramp-file-name-host vec) remote-local) - - ;; Retry loop for reliability - (while (and (not success) (< retries tramp-rpc-deploy-max-retries)) - (let ((attempt (1+ retries))) - (message "Transfer attempt %d/%d..." attempt tramp-rpc-deploy-max-retries) - (condition-case err - (progn - ;; Use TRAMP's copy-file for binary transfer (via sshx bootstrap method) - ;; This is much more reliable than base64 encoding through heredocs - (copy-file local-path remote-tmp-path t) - - ;; Verify the file was created and has content - (unless (tramp-send-command-and-check - vec - (format "test -s %s" (tramp-shell-quote-argument remote-tmp-local))) - (error "Temp file not created or is empty after copy")) - - ;; Verify checksum - (let ((remote-checksum (tramp-rpc-deploy--remote-checksum vec remote-tmp-local))) - (unless remote-checksum - (error "Could not compute remote checksum (sha256sum/shasum not available?)")) - (if (string= local-checksum remote-checksum) - (progn - ;; Checksum matches - make executable and atomically move - (tramp-send-command - vec - (format "chmod +x %s && mv -f %s %s" - (tramp-shell-quote-argument remote-tmp-local) - (tramp-shell-quote-argument remote-tmp-local) - (tramp-shell-quote-argument remote-local))) - (setq success t) - (message "Transfer completed successfully")) - ;; Checksum mismatch - clean up and retry - (let ((err-msg (format "Attempt %d: Checksum mismatch (local: %s, remote: %s)" - attempt - (substring local-checksum 0 12) - (substring remote-checksum 0 12)))) - (push err-msg errors) - (message "%s" err-msg)) - (ignore-errors (delete-file remote-tmp-path)) - (setq retries (1+ retries))))) - (error - ;; Clean up on error and retry - (let ((err-msg (format "Attempt %d: %s" attempt (error-message-string err)))) - (push err-msg errors) - (message "Transfer error: %s" err-msg)) - (ignore-errors (delete-file remote-tmp-path)) - (setq retries (1+ retries)))))) - - (unless success - (error "Failed to transfer binary after %d attempts.\n\nErrors:\n%s\n\nTroubleshooting:\n- Verify SSH access: ssh %s@%s echo success\n- Check write permissions to %s on remote host\n- Ensure sha256sum or shasum command is available on remote host" - tramp-rpc-deploy-max-retries - (mapconcat #'identity (nreverse errors) "\n") - (or (tramp-file-name-user vec) "USER") - (tramp-file-name-host vec) - tramp-rpc-deploy-remote-directory)) - - remote-path)) - -;;; ============================================================================ -;;; Public API -;;; ============================================================================ - -(defun tramp-rpc-deploy-ensure-binary (vec) - "Ensure the tramp-rpc-server binary is available on remote VEC. -Returns the remote path to the binary. -If `tramp-rpc-deploy-auto-deploy' is nil and the binary is missing, -signals an error." - (let ((bootstrap-vec (tramp-rpc-deploy--bootstrap-vec vec))) - (if (tramp-rpc-deploy--remote-binary-exists-p bootstrap-vec) - ;; Binary already exists - (tramp-file-local-name (tramp-rpc-deploy--remote-binary-path bootstrap-vec)) - ;; Need to deploy - (if tramp-rpc-deploy-auto-deploy - (let* ((arch (tramp-rpc-deploy--detect-remote-arch bootstrap-vec)) - (local-binary (tramp-rpc-deploy--ensure-local-binary arch))) - (message "Deploying tramp-rpc-server (%s) to %s..." - arch (tramp-file-name-host vec)) - (tramp-file-local-name - (tramp-rpc-deploy--transfer-binary bootstrap-vec local-binary))) - (error "tramp-rpc-server not found on %s and auto-deploy is disabled" - (tramp-file-name-host vec)))))) - -(defun tramp-rpc-deploy-remove-binary (vec) - "Remove the tramp-rpc-server binary from remote VEC." - (interactive - (list (tramp-dissect-file-name - (read-file-name "Remote host: " "/ssh:")))) - (let ((bootstrap-vec (tramp-rpc-deploy--bootstrap-vec vec))) - (when (tramp-rpc-deploy--remote-binary-exists-p bootstrap-vec) - (tramp-send-command - bootstrap-vec - (format "rm -f %s" - (tramp-shell-quote-argument - (tramp-file-local-name - (tramp-rpc-deploy--remote-binary-path bootstrap-vec))))) - (message "Removed %s from %s" - tramp-rpc-deploy-binary-name - (tramp-file-name-host vec))))) - -(defun tramp-rpc-deploy-clear-cache () - "Clear the local binary cache." - (interactive) - (when (file-exists-p tramp-rpc-deploy-local-cache-directory) - (delete-directory tramp-rpc-deploy-local-cache-directory t) - (message "Cleared tramp-rpc binary cache"))) - -(defun tramp-rpc-deploy-status () - "Show the status of tramp-rpc-server binaries." - (interactive) - (let ((buf (get-buffer-create "*tramp-rpc-deploy-status*"))) - (with-current-buffer buf - (erase-buffer) - (insert "TRAMP-RPC Server Deployment Status\n") - (insert "===================================\n\n") - (insert (format "Version: %s\n" tramp-rpc-deploy-version)) - (insert (format "Local arch: %s\n" (tramp-rpc-deploy--detect-local-arch))) - (insert (format "Cargo available: %s\n" - (if (tramp-rpc-deploy--cargo-available-p) "yes" "no"))) - (insert (format "Source directory: %s\n" - (or tramp-rpc-deploy-source-directory "not set"))) - (insert (format "Cache directory: %s\n\n" tramp-rpc-deploy-local-cache-directory)) - - (insert "Cached Binaries:\n") - (insert "----------------\n") - (dolist (arch '("x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin")) - (let ((path (tramp-rpc-deploy--local-cache-path arch))) - (insert (format " %s: %s\n" - arch - (if (file-exists-p path) - (format "cached (%s)" - (file-size-human-readable - (file-attribute-size (file-attributes path)))) - "not cached"))))) - (insert "\n") - (insert "Download URLs:\n") - (insert "--------------\n") - (dolist (arch '("x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin")) - (insert (format " %s:\n %s\n" arch (tramp-rpc-deploy--download-url arch))))) - (display-buffer buf))) - -(defun tramp-rpc-deploy-diagnose (host &optional user) - "Run diagnostics for deploying to HOST. -Optional USER specifies the SSH user. -This helps troubleshoot deployment issues." - (interactive "sHost: \nsUser (leave empty for default): ") - (when (string-empty-p user) - (setq user nil)) - (let ((buf (get-buffer-create "*tramp-rpc-diagnose*"))) - (with-current-buffer buf - (erase-buffer) - (insert (format "TRAMP-RPC Deployment Diagnostics for %s%s\n" - (if user (concat user "@") "") host)) - (insert "=" (make-string 50 ?=) "\n\n") - - ;; Test 1: SSH connectivity - (insert "1. Testing SSH connectivity...\n") - (let* ((ssh-cmd (append - (list "ssh" "-o" "BatchMode=yes" "-o" "ConnectTimeout=10") - (when user (list "-l" user)) - (list host "echo 'SSH_OK'"))) - (output (with-temp-buffer - (apply #'call-process (car ssh-cmd) nil t nil (cdr ssh-cmd)) - (buffer-string)))) - (if (string-match-p "SSH_OK" output) - (insert " [OK] SSH connection successful\n") - (insert " [FAIL] SSH connection failed\n") - (insert (format " Output: %s\n" (string-trim output))))) - - ;; Test 2: Remote architecture - (insert "\n2. Detecting remote architecture...\n") - (let* ((ssh-cmd (append - (list "ssh" "-o" "BatchMode=yes") - (when user (list "-l" user)) - (list host "uname -m && uname -s"))) - (output (with-temp-buffer - (if (zerop (apply #'call-process (car ssh-cmd) nil t nil (cdr ssh-cmd))) - (buffer-string) - "FAILED")))) - (if (string-match-p "FAILED" output) - (insert " [FAIL] Could not detect architecture\n") - (insert (format " [OK] Architecture: %s\n" (string-trim output))))) - - ;; Test 3: Remote directory writable - (insert "\n3. Testing remote directory access...\n") - (let* ((dir tramp-rpc-deploy-remote-directory) - (ssh-cmd (append - (list "ssh" "-o" "BatchMode=yes") - (when user (list "-l" user)) - (list host (format "mkdir -p %s && test -w %s && echo 'WRITABLE'" - (shell-quote-argument dir) - (shell-quote-argument dir))))) - (output (with-temp-buffer - (apply #'call-process (car ssh-cmd) nil t nil (cdr ssh-cmd)) - (buffer-string)))) - (if (string-match-p "WRITABLE" output) - (insert (format " [OK] Directory %s is writable\n" dir)) - (insert (format " [FAIL] Directory %s not writable\n" dir)))) - - ;; Test 4: Checksum command - (insert "\n4. Testing checksum command availability...\n") - (let* ((ssh-cmd (append - (list "ssh" "-o" "BatchMode=yes") - (when user (list "-l" user)) - (list host "which sha256sum || which shasum || echo 'NONE'"))) - (output (with-temp-buffer - (apply #'call-process (car ssh-cmd) nil t nil (cdr ssh-cmd)) - (string-trim (buffer-string))))) - (if (string-match-p "NONE" output) - (insert " [FAIL] No checksum command found (need sha256sum or shasum)\n") - (insert (format " [OK] Found: %s\n" output)))) - - ;; Test 5: Local binary availability - (insert "\n5. Checking local binary cache...\n") - (dolist (arch '("x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin")) - (let ((path (tramp-rpc-deploy--local-cache-path arch)) - (bundled (tramp-rpc-deploy--bundled-binary-path arch))) - (cond - ((and bundled (file-exists-p bundled)) - (insert (format " [OK] %s: bundled binary available\n" arch))) - ((file-exists-p path) - (insert (format " [OK] %s: cached at %s\n" arch path))) - (t - (insert (format " [ ] %s: not available locally\n" arch)))))) - - (insert "\n\nIf deployment fails, try:\n") - (insert " 1. Enable debug logging: (setq tramp-rpc-deploy-debug t)\n") - (insert " 2. Retry the connection and check *tramp-rpc-deploy* buffer\n") - (insert " 3. Manually test: ssh " (if user (concat user "@") "") host " echo success\n")) - (display-buffer buf))) - -(provide 'tramp-rpc-deploy) -;;; tramp-rpc-deploy.el ends here |
