This is possibly one of the most complicated support questions that can be asked, the only real answer is it depends.
It’s really a question of which kind of shell Capistrano is using, it’s a matrix of possibilities concerning login
, non-login
, interactive
, or non-interactive
.
By default Capistrano always assigns a non-login
, non-interactive
shell.
What are Linux Shell Modes?
Unix shells can be started in one of three modes, an unnamed basic mode, which almost never happens, as a login
shell, or as an interactive
shell.
Depending which mode a shell starts in (and which shell you are using) this will affect which startup (more commonly known as dot-files) files, if any are loaded, here’s more or less the matrix of what is loaded when.
What about the Capistrano option to assign a pty
?
This option has been hugely misleadingly used, if you ask SSH to provide a pty
you are effectively telling SSH that “I’ll connect this session to a user terminal”, thus programs on the receiving end expect that they can prompt for input, and provide coloured output, etc. In short they think they’re talking to you over an interactive session, because by assigning a pty
, Bash has been started in non-login
, interactive
mode.
Read more about this:
- In the “Bash Startup Files” section of the Bash manual
- At Sam Stephenson’s excellent Unix shell initialization wiki page
- Interactive and non-interactive shells and scripts documentation
How does what Capistrano does differ from an SSH session
By default Capistrano prefers to start a non-login, non-interactive shell, to try and isolate the environment and make sure that things work as expected, regardless of any changes that might happen on the server side.
In contrast when you log into a machine with your terminal, into a regular Bash session, the --login
option to Bash is implied granting you a login
shell, and because you are in a terminal, ssh asks the ssh server to provide a pty so that you may start an interactive session. Thus you get an interactive login
shell, the exact opposite of what we need for Capistrano!
How can I check?
I actually had to look this up, most of the time it’s common sense, but stackoverflow to the rescue, let’s figure this out!
First, we’ll try a real SSH session, logging in via our terminal, and seeing what happens:
1 2 3 4 5 6 |
me@localhost <span class="nv">$ </span>ssh me@remote me@remote <span class="nv">$ </span><span class="o">[[</span> <span class="nv">$-</span> <span class="o">==</span> <span class="k">*</span>i<span class="k">*</span> <span class="o">]]</span> <span class="o">&&</span> <span class="nb">echo</span> <span class="s1">'Interactive'</span> <span class="o">||</span> <span class="nb">echo</span> <span class="s1">'Not interactive'</span> Interactive me@remote <span class="nv">$ </span><span class="nb">shopt</span> <span class="nt">-q</span> login_shell <span class="o">&&</span> <span class="nb">echo</span> <span class="s1">'Login shell'</span> <span class="o">||</span> <span class="nb">echo</span> <span class="s1">'Not login shell'</span> Login shell |
Contrast that with what happens when we hand the command to run to the SSH command line without logging in first…
1 2 3 4 5 |
me@localhost <span class="nv">$ </span>ssh me@remote <span class="s2">"[[ </span><span class="nv">$-</span><span class="s2"> == *i* ]] && echo 'Interactive' || echo 'Not interactive'"</span> Interactive me@localhost <span class="nv">$ </span>ssh me@remote <span class="s2">"shopt -q login_shell && echo 'Login shell' || echo 'Not login shell'"</span> Not login shell |
Here we can see that Bash is still starting in interactive mode when we’re just running a single command, that’s because the terminal we are using is interactive, and SSH inherits that and passes that on to the remote server.
When we try the same with Capistrano we’ll see yet another set of results; we can have a very simple, Capfile, we don’t even need to load the default recipes to test this:
1 2 3 4 5 6 7 8 9 10 11 12 |
<span class="c1"># Capistrano 3</span> <span class="n">task</span> <span class="ss">:query_interactive</span> <span class="k">do</span> <span class="n">on</span> <span class="s1">'me@remote'</span> <span class="k">do</span> <span class="n">info</span> <span class="n">capture</span><span class="p">(</span><span class="s2">"[[ $- == *i* ]] && echo 'Interactive' || echo 'Not interactive'"</span><span class="p">)</span> <span class="k">end</span> <span class="k">end</span> <span class="n">task</span> <span class="ss">:query_login</span> <span class="k">do</span> <span class="n">on</span> <span class="s1">'me@remote'</span> <span class="k">do</span> <span class="n">info</span> <span class="n">capture</span><span class="p">(</span><span class="s2">"shopt -q login_shell && echo 'Login shell' || echo 'Not login shell'"</span><span class="p">)</span> <span class="k">end</span> <span class="k">end</span> |
Gives us the following:
1 2 3 4 5 |
me@localhost <span class="nv">$ </span>cap query_login INFO Not login shell me@localhost <span class="nv">$ </span>cap query_interactive INFO Not interactive |
Which shell startup files do get loaded?
Best explained with this diagram, yes it’s that complicated: