Here-doc headache
I was working on the deployment pipeline for a service that launches an app in a dedicated VM using GitHub Actions. In the last step of the workflow, the CI SSHs into the VM and runs several commands using a here document in bash. The simplified version looks like this:
The fully working version can be found in the serve-init repo with here-doc.
Here, environment variables like SSH_USER, SSH_HOST, and APP_AUTH_TOKEN are defined in the surrounding local scope of the CI. The variables then get propagated to the remote machine when we run the commands via here-doc.
However, I couldn't figure out why the Docker containers weren't able to access the value of the AUTH_TOKEN variable. The other variables were getting through just fine.
It turns out, export AUTH_TOKEN=$AUTH_TOKEN within the here-doc block, doesn't export the variable in the remote shell. So this doesn't do what I thought it would:
I was expecting it to print:
But instead, it just prints:
So export FOO=bar in the here-doc block doesn't set the variable in the remote shell. One solution is to set it before the block like this:
This prints:
So, in the CI pipeline, we could do the following to propagate the environment variable from local to the remote machine:
This will print the value of the environment variable on the remote machine correctly. However, this doesn't set the value in the remote shell's environment. If you SSH into the remote machine and try to print the variable's value, you'll see nothing gets printed. The previous command only passes the value to the remote machine temporarily and doesn't set it permanently in the remote shell.
To fix it, you could pipe the value into a file and load it in the remote shell like this:
Here, echo $FOO instead of echo $FOO ensures that the shell expansion is done on the remote machine, not on the local. This allows us to know that the environment variable has been set in the remote shell correctly.
Maybe the behavior makes sense, but it still broke my mental model.
So I decided to get rid of here-doc in the pipeline altogether and went with this:
One thing to keep in mind with the second approach is that if you need to run any expanded commands, you'll need to defer it with a backslash so that it's run on the remote machine, not on the local:
Without the backslash, the $(...) will be expanded on the local machine, which is not desirable here. The backslash defers it so that it runs on the remote instead.
Discussion in the ATmosphere