Search code examples
bashdockerubuntudockerfileros

source in every Dockerfile RUN call


I have a Dockerfile, where I am finding myself constantly needing to call source /opt/ros/noetic/setup.bash.

e.g.:

RUN source /opt/ros/noetic/setup.bash \
    && SOME_COMMAND

RUN source /opt/ros/noetic/setup.bash \
    && SOME_OTHER_COMMAND

Is there a method to have this initialised in every RUN call in a Dockerfile?

Have tried adding to ~/.bash_profile and Docker's ENV command with no luck.


Solution

  • TL;DR: what you want is feasible by copying your .sh script in /etc/profile.d/ and using the SHELL Dockerfile command to tweak the default shell.

    Details:

    To start with, consider the following sample script setup.sh:

    #!/bin/sh
    echo "# setup.sh"
    ENV_VAR="some value"
    some_fun() {
        echo "## some_fun"
    }
    

    Then, it can be noted that bash provides the --login CLI option:

    When Bash is invoked as an interactive login shell, or as a non-interactive shell with the --login option, it first reads and executes commands from the file /etc/profile, if that file exists. After reading that file, it looks for ~/.bash_profile, ~/.bash_login, and ~/.profile, in that order, and reads and executes commands from the first one that exists and is readable. The --noprofile option may be used when the shell is started to inhibit this behavior.

    When an interactive login shell exits, or a non-interactive login shell executes the exit builtin command, Bash reads and executes commands from the file ~/.bash_logout, if it exists.

    https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Bash-Startup-Files

    Furthermore, instead of appending the setup.sh code in /etc/profile, you can take advantage of the /etc/profile.d folder that is read in the following way by most distributions:

    $ docker run --rm -i debian:10 cat /etc/profile | tail -n 9
    
    if [ -d /etc/profile.d ]; then
      for i in /etc/profile.d/*.sh; do
        if [ -r $i ]; then
          . $i
        fi
      done
      unset i
    fi
    

    Note in particular that the .sh extension is mandatory, hence the naming of the minimal-working-example above: setup.sh (not setup.bash).

    Finally, it is possible to rely on the SHELL command to replace the default shell used by RUN (in place of ["/bin/sh", "-c"]) to incorporate the --login option of bash.

    Concretely, you could phrase your Dockerfile like this:

    FROM debian:10
    
    # WORKDIR /opt/ros/noetic
    # COPY setup.sh .
    # RUN . /opt/ros/noetic/setup.sh && echo "ENV_VAR=$ENV_VAR"
    
    # empty var here
    RUN echo "ENV_VAR=$ENV_VAR"
    
    # enable the extra shell init code
    COPY setup.sh /etc/profile.d/
    
    SHELL ["/bin/bash", "--login", "-c"]
    
    # nonempty var and function
    RUN echo "ENV_VAR=$ENV_VAR" && some_fun
    
    # DISABLE the extra shell init code!
    RUN rm /etc/profile.d/setup.sh
    
    # empty var here
    RUN echo "ENV_VAR=$ENV_VAR"
    

    Outcome:

    $ docker build -t test .
    Sending build context to Docker daemon  6.144kB
    Step 1/7 : FROM debian:10
     ---> ef05c61d5112
    Step 2/7 : RUN echo "ENV_VAR=$ENV_VAR"
     ---> Running in 87b5c589ec60
    ENV_VAR=
    Removing intermediate container 87b5c589ec60
     ---> 6fdb70be76f9
    Step 3/7 : COPY setup.sh /etc/profile.d/
     ---> e6aab4ebf9ef
    Step 4/7 : SHELL ["/bin/bash", "--login", "-c"]
     ---> Running in d73b0d13df23
    Removing intermediate container d73b0d13df23
     ---> ccbe789dc36d
    Step 5/7 : RUN echo "ENV_VAR=$ENV_VAR" && some_fun
     ---> Running in 42fd1ae14c17
    # setup.sh
    ENV_VAR=some value
    ## some_fun
    Removing intermediate container 42fd1ae14c17
     ---> de74831896a4
    Step 6/7 : RUN rm /etc/profile.d/setup.sh
     ---> Running in bdd969a63def
    # setup.sh
    Removing intermediate container bdd969a63def
     ---> 5453be3271e5
    Step 7/7 : RUN echo "ENV_VAR=$ENV_VAR"
     ---> Running in 0712cea427f1
    ENV_VAR=
    Removing intermediate container 0712cea427f1
     ---> 216a421f5659
    Successfully built 216a421f5659
    Successfully tagged test:latest