From unknown Mon Jun 23 02:24:38 2025 X-Loop: help-debbugs@gnu.org Subject: bug#62326: tramp-container PATH logic is incorrect Resent-From: ParetoOptimalDev Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Tue, 21 Mar 2023 08:30:03 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: report 62326 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: 62326@debbugs.gnu.org X-Debbugs-Original-To: bug-gnu-emacs@gnu.org Received: via spool by submit@debbugs.gnu.org id=B.16793873612701 (code B ref -1); Tue, 21 Mar 2023 08:30:03 +0000 Received: (at submit) by debbugs.gnu.org; 21 Mar 2023 08:29:21 +0000 Received: from localhost ([127.0.0.1]:57556 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1peXMs-0000hN-6T for submit@debbugs.gnu.org; Tue, 21 Mar 2023 04:29:21 -0400 Received: from lists.gnu.org ([209.51.188.17]:47838) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1peTYb-0002fG-Rr for submit@debbugs.gnu.org; Tue, 21 Mar 2023 00:25:10 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1peTYb-0007J5-FW for bug-gnu-emacs@gnu.org; Tue, 21 Mar 2023 00:25:09 -0400 Received: from ciao.gmane.io ([116.202.254.214]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1peTYZ-0002f4-E6 for bug-gnu-emacs@gnu.org; Tue, 21 Mar 2023 00:25:09 -0400 Received: from list by ciao.gmane.io with local (Exim 4.92) (envelope-from ) id 1peTYU-00025s-HW for bug-gnu-emacs@gnu.org; Tue, 21 Mar 2023 05:25:02 +0100 X-Injected-Via-Gmane: http://gmane.org/ From: ParetoOptimalDev Date: Mon, 20 Mar 2023 16:58:22 -0500 Message-ID: <871qljultt.fsf@mailfence.com> Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit User-Agent: Gnus/5.13 (Gnus v5.13) Cancel-Lock: sha1:Y65ZHpfqdt+psUkRc889OybF+YQ= Received-SPF: pass client-ip=116.202.254.214; envelope-from=geb-bug-gnu-emacs@m.gmane-mx.org; helo=ciao.gmane.io X-Spam_score_int: 0 X-Spam_score: -0.1 X-Spam_bar: / X-Spam_report: (-0.1 / 5.0 requ) BAYES_00=-1.9, DATE_IN_PAST_06_12=1.543, HEADER_FROM_DIFFERENT_DOMAINS=0.25, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-Spam-Score: -0.0 (/) X-Mailman-Approved-At: Tue, 21 Mar 2023 04:29:17 -0400 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) For docker containers tramp should set the PATH equal to whatever `ENV PATH=...` is in the Dockerfile. To be precise it should match the output of: ``` docker inspect --format='{{json .Config.Env}} $docker_container_id ``` Right now however, there is a piece of logic in tramp-get-remote-path that removes non-existent directories: (delq nil (mapcar (lambda (x) (and (stringp x) (file-directory-p (tramp-make-tramp-file-name vec x)) x)) remote-path)) This seems reasonable, but in practice it doesn't seem to work. Here is a script to demonstrate with the haskell container from dockerhub: (require 'tramp) ;; pre-require things that were making loading messages that clutter up the batch output (require 'em-alias) (require 'em-banner) (require 'em-basic) (require 'em-cmpl) (require 'em-extpipe) (require 'em-glob) (require 'em-hist) (require 'em-ls) (require 'em-pred) (require 'em-prompt) (require 'em-script) (require 'em-term) (require 'em-unix) (setq edebug-print-length 500) (setq tramp-verbose 7) (setq existing-haskell-container-name "nifty_davinci") (defun start-new-haskell-container () """ returns container id""" (string-trim (shell-command-to-string "docker run -i --rm --detach haskell:slim@sha256:68280eb4fd3d4ee1b62cf40619fd6e956a741f693ef53e9dd4168d2c721880f5"))) (setq docker-container-id (or existing-haskell-container-name (start-new-haskell-container))) (message (format "emacs-version: %s" emacs-version)) (message (format "tramp-version: %s" tramp-version)) (message "") (message "using the docker haskell image") (message "expected path is:") (message (string-trim (shell-command-to-string (format "docker inspect --format='{{json .Config.Env}}' %s" docker-container-id)))) (let ((default-directory (concat "/docker:" docker-container-id ":")) (tramp-remote-path '(tramp-own-remote-path))) (tramp-cleanup-all-connections) (message (concat "path is: " (truncate-string-to-width (eshell-command-result "echo $PATH") 100)))) Here is what happens when I run the code above: $ emacs -Q --batch --load config.el emacs-version: 30.0.50 tramp-version: 2.7.0-pre using the docker haskell image expected path is: ["PATH=/root/.cabal/bin:/root/.local/bin:/opt/ghc/9.4.4/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","LANG=C.UTF-8"] Tramp: Sending command ‘exec docker exec -it a896e8aacaf0adaedd49b9bcd6b8104d7b5c4343d08382b4912ffaa8192bbb81 /bin/sh -i’ Tramp: Found remote shell prompt on ‘a896e8aacaf0adaedd49b9bcd6b8104d7b5c4343d08382b4912ffaa8192bbb81’ path is: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin You can see that crucial directories containing the Haskell compiler and build tool are missing. I'm not sure why they don't exist. But I can verify If I remove the logic and preserve the non-existent directories things work: (defun my/tramp-get-remote-path (vec) "Compile list of remote directories for PATH. Nonexistent directories are removed from spec." (with-current-buffer (tramp-get-connection-buffer vec) ;; Expand connection-local variables. (tramp-set-connection-local-variables vec) (with-tramp-connection-property ;; When `tramp-own-remote-path' is in `tramp-remote-path', we ;; cache the result for the session only. Otherwise, the ;; result is cached persistently. (if (memq 'tramp-own-remote-path tramp-remote-path) (tramp-get-process vec) vec) "remote-path" (let* ((remote-path (copy-tree tramp-remote-path)) (elt1 (memq 'tramp-default-remote-path remote-path)) (elt2 (memq 'tramp-own-remote-path remote-path)) (default-remote-path (when elt1 (or (tramp-send-command-and-read vec (format "echo \\\"`getconf PATH 2>%s`\\\"" (tramp-get-remote-null-device vec)) 'noerror) ;; Default if "getconf" is not available. (progn (tramp-message vec 3 "`getconf PATH' not successful, using default value \"%s\"." "/bin:/usr/bin") "/bin:/usr/bin")))) (own-remote-path ;; The login shell could return more than just the $PATH ;; string. So we use `tramp-end-of-heredoc' as marker. (when elt2 (or (tramp-send-command-and-read vec (format "%s %s %s 'echo %s \\\"$PATH\\\"'" (tramp-get-method-parameter vec 'tramp-remote-shell) (string-join (tramp-get-method-parameter vec 'tramp-remote-shell-login) " ") (string-join (tramp-get-method-parameter vec 'tramp-remote-shell-args) " ") (tramp-shell-quote-argument tramp-end-of-heredoc)) 'noerror (rx (literal tramp-end-of-heredoc))) (progn (tramp-message vec 2 "Could not retrieve `tramp-own-remote-path'") nil))))) ;; Replace place holder `tramp-default-remote-path'. (when elt1 (setcdr elt1 (append (split-string (or default-remote-path "") ":" 'omit) (cdr elt1))) (setq remote-path (delq 'tramp-default-remote-path remote-path))) ;; Replace place holder `tramp-own-remote-path'. (when elt2 (setcdr elt2 (append (split-string (or own-remote-path "") ":" 'omit) (cdr elt2))) (setq remote-path (delq 'tramp-own-remote-path remote-path))) ;; Remove double entries. (setq elt1 remote-path) (while (consp elt1) (while (and (car elt1) (setq elt2 (member (car elt1) (cdr elt1)))) (setcar elt2 nil)) (setq elt1 (cdr elt1))) (delq nil remote-path))))) (advice-add 'tramp-get-remote-path :override 'my/tramp-get-remote-path) I'm not sure if `Remove non-existing directories.` exists for a particular reason or not, but I'd argue it's not appropriate for docker containers. I'm surprised that there aren't more bug reports along these lines since this should affect anyone working with tramp, eshell, and eglot or lsp-mode. Thanks