Skip to content

Commit fb3f558

Browse files
committed
Prompt for SSH passphrases in minibuffer
1 parent 4128d42 commit fb3f558

File tree

1 file changed

+136
-14
lines changed

1 file changed

+136
-14
lines changed

straight.el

Lines changed: 136 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,23 @@ be interpreted later as a symlink."
10031003
link-name (file-symlink-p link-name) link-target))))))
10041004

10051005
;;;;; External processes
1006+
1007+
(defcustom straight-display-subprocess-prompts nil
1008+
"Non-nil means display prompts received from subprocess invocations.
1009+
For example, if a Git clone requires HTTPS or SSH passphrase to be
1010+
entered, you will be prompted in the minibuffer, and your response will
1011+
be passed back to Git. Nil means stdin will not be connected to
1012+
subprocesses, so for example Git will fall back to non-tty
1013+
authentication if possible, or fail otherwise.
1014+
1015+
The default value is nil for now because the code for handling
1016+
interactive subprocesses is much more complex, and might have unintended
1017+
side effects, so it is currently being tested for robustness. Please
1018+
report any bugs you find: performance regressions, execution hangs, and
1019+
out-of-order command execution are the most likely unintended side
1020+
effects of enabling this option."
1021+
:type 'boolean)
1022+
10061023
(defvar straight--process-log t
10071024
"If non-nil, log process output to `straight-process-buffer'.")
10081025

@@ -1020,6 +1037,62 @@ against the wrong repositories.
10201037
If you set this globally to something other than nil, you may be
10211038
eaten by a grue.")
10221039

1040+
(defvar straight--process-output-counter 0
1041+
"Variable incremented each time output is received from a process.
1042+
This is a way of seeing whether output was received from a process
1043+
during a call to `accept-process-output'.")
1044+
1045+
(defun straight--process-filter (proc string)
1046+
"Process filter for interactive processes spawned by straight.el.
1047+
Outputs to the process buffer, but directs username and passphrase
1048+
prompts to the minibuffer, like Magit. See [1] in comment below for
1049+
information on PROC and STRING."
1050+
(cl-incf straight--process-output-counter)
1051+
;; [1]: https://www.gnu.org/software/emacs/manual/html_node/elisp/Filter-Functions.html
1052+
(cl-block nil
1053+
(condition-case _
1054+
(when-let ((buf (process-buffer proc)))
1055+
(with-current-buffer buf
1056+
(save-excursion
1057+
(goto-char (point-max))
1058+
(let ((case-fold-search t)
1059+
(inhibit-read-only t)
1060+
(stdin (plist-get (process-plist proc) :stdin)))
1061+
;; Here is the part where we try to emulate a very
1062+
;; small part of the behavior of a real terminal
1063+
;; emulator, and handle carriage returns correctly.
1064+
;; (In other words, don't spew a bunch of garbage
1065+
;; during a 'git clone' because of the progress bar
1066+
;; messages.)
1067+
(let ((parts (split-string string "\r")))
1068+
(insert (car parts))
1069+
(dolist (part (cdr parts))
1070+
(delete-region
1071+
(line-beginning-position) (line-end-position))
1072+
(insert part)))
1073+
(let ((prompt (thing-at-point 'line)))
1074+
(when (string-match "username.*: $" prompt)
1075+
(let ((username (read-string (thing-at-point 'line))))
1076+
(insert username "\n")
1077+
(ignore-errors
1078+
(process-send-string stdin (concat username "\n")))
1079+
(cl-return))))
1080+
(let ((prompt (thing-at-point 'line)))
1081+
(when (string-match
1082+
"\\(password\\|passphrase\\).*: $" prompt)
1083+
(let ((password (read-passwd prompt)))
1084+
(insert (make-string
1085+
(length password)
1086+
(or (bound-and-true-p read-hide-char) ?*)))
1087+
(ignore-errors
1088+
(process-send-string stdin (concat password "\n")))
1089+
(clear-string password)
1090+
(cl-return))))))))
1091+
(quit
1092+
(ignore-errors
1093+
(set-process-filter proc nil)
1094+
(interrupt-process proc))))))
1095+
10231096
(defconst straight--process-stderr
10241097
(expand-file-name (format "straight-stderr-%s" (emacs-pid))
10251098
temporary-file-directory)
@@ -1049,26 +1122,75 @@ batch mode."
10491122
(unless (derived-mode-p 'special-mode) (special-mode))
10501123
(current-buffer)))
10511124

1125+
(defun straight--process-call-interactively (program &rest args)
1126+
"Run PROGRAM synchronously with ARGS.
1127+
Return a list of form: (EXITCODE STDOUT STDERR)."
1128+
(let* ((stdout (get-buffer-create " *straight-proc-stdout*"))
1129+
(stderr (get-buffer-create " *straight-proc-stderr*"))
1130+
(_ (dolist (buf (list stdout stderr))
1131+
(let ((proc (get-buffer-process buf)))
1132+
(ignore-errors
1133+
(set-process-filter proc #'ignore)
1134+
(kill-process proc)))))
1135+
(stdout-proc (make-process
1136+
:name "straight"
1137+
:command (cons program args)
1138+
:buffer stdout
1139+
:stderr stderr
1140+
:noquery t))
1141+
(stderr-proc (get-buffer-process stderr)))
1142+
(dolist (buf (list stdout stderr))
1143+
(with-current-buffer buf
1144+
(erase-buffer))
1145+
(let ((proc (get-buffer-process buf)))
1146+
(set-process-filter proc #'straight--process-filter)
1147+
(set-process-sentinel proc #'ignore)
1148+
(set-process-plist
1149+
proc (plist-put (process-plist proc) :stdin stdout-proc))))
1150+
(setq straight--process-output-counter 0)
1151+
(let ((last-output-counter -1))
1152+
(while (or (process-live-p stdout-proc)
1153+
(process-live-p stderr-proc)
1154+
(/= straight--process-output-counter
1155+
last-output-counter))
1156+
;; Save the previous value of the output counter, then run
1157+
;; `accept-process-output'. If the counter doesn't increase
1158+
;; when doing so, then no output was received, which according
1159+
;; to the API of `accept-process-output' means that the
1160+
;; connection must be closed, and we are done.
1161+
(setq last-output-counter straight--process-output-counter)
1162+
(accept-process-output stdout-proc)
1163+
(accept-process-output stderr-proc)))
1164+
(list
1165+
(process-exit-status stdout-proc)
1166+
(with-current-buffer stdout
1167+
(buffer-string))
1168+
(with-current-buffer stderr
1169+
(buffer-string)))))
1170+
10521171
(defun straight--process-call (program &rest args)
1053-
"Run PROGRAM syncrhonously with ARGS.
1172+
"Run PROGRAM synchronously with ARGS.
10541173
Return a list of form: (EXITCODE STDOUT STDERR).
10551174
If the process is unable to start, return an elisp error object."
10561175
(when (string-match-p "/" program)
10571176
(setq program (expand-file-name program)))
1058-
(condition-case e
1059-
(with-temp-buffer
1060-
(list
1061-
(apply #'call-process program nil
1062-
(list t straight--process-stderr)
1063-
nil args)
1064-
(let ((s (buffer-substring-no-properties (point-min) (point-max))))
1065-
(unless (string-empty-p s) s))
1066-
(progn
1067-
(insert-file-contents
1068-
straight--process-stderr nil nil nil 'replace)
1177+
(if straight-display-subprocess-prompts
1178+
(apply #'straight--process-call-interactively program args)
1179+
(condition-case e
1180+
(with-temp-buffer
1181+
(list
1182+
(apply #'call-process program nil
1183+
(list t straight--process-stderr)
1184+
nil args)
10691185
(let ((s (buffer-substring-no-properties (point-min) (point-max))))
1070-
(unless (string-empty-p s) s)))))
1071-
(error e)))
1186+
(unless (string-empty-p s) s))
1187+
(progn
1188+
(insert-file-contents
1189+
straight--process-stderr nil nil nil 'replace)
1190+
(let ((s (buffer-substring-no-properties
1191+
(point-min) (point-max))))
1192+
(unless (string-empty-p s) s)))))
1193+
(error e))))
10721194

10731195
(defmacro straight--process-with-result (result &rest body)
10741196
"Provide anaphoric RESULT bindings for duration of BODY.

0 commit comments

Comments
 (0)