1

I'm trying to set a variable in a RUN command and use it in the next RUN command.

Depending on the TARGETARCH arg, I'm trying to set PROTOC_ARCH variable and use it in the next RUN command to download a architecture specific file. But the variable isn't passing through. If I print the variable, it's always empty.

FROM ubuntu

ARG TARGETARCH="arm64"

ENV PROTOC_ARCH=""

RUN if [ "$TARGETARCH" = "arm64" ]; then \
       PROTOC_ARCH="aarch_64"; \
    else \
       PROTOC_ARCH="x86_64"; \
    fi

RUN apt-get update && apt-get install -y wget

RUN echo "PROTOC_ARCH=$PROTOC_ARCH"

RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v22.2/protoc-22.2-linux-$PROTOC_ARCH.zip

Can anyone please tell me what I'm doing wrong here ?

Thanks in advance.

Masudur Rahman
  • 1,585
  • 8
  • 16
  • 2
    You can't, and you don't. Environment variables only go from one process to its children -- not back to parents, not to siblings. Each `RUN` is a new, separate process; when you define an environment variable in an interactive shell and use it later, you're using it later _in that same shell_; you can't use it in a different shell you start after the first one exits. When a RUN finishes, the processes it started have all exited. – Charles Duffy Mar 30 '23 at 18:03
  • @CharlesDuffy, Is there any way I could achieve the ultimate goal here ? – Masudur Rahman Mar 30 '23 at 18:07
  • 2
    Think about something like `echo aarch64 >/.protoc_arch` or `echo x86_64 >/.protoc_arch`, and then later `echo "PROTOC_ARCH=$(cat /.protoc_arch)"` or `RUN wget "https://.../protoc-2.22-linux-$(cat /.protoc_arch).zip"` – Charles Duffy Mar 30 '23 at 18:07
  • add the `wget` and `echo` to the same `RUN` that sets `PROTOC_ARCH`. – erik258 Mar 30 '23 at 18:11
  • @erik258, in my actual Dockerfile I've some other stuff depending on the condition. So, I wanted to run the command in a separate RUN command. – Masudur Rahman Mar 30 '23 at 18:27
  • Also see [Conditional ENV in Dockerfile](https://stackoverflow.com/questions/37057468/conditional-env-in-dockerfile) which is similar in spirit. – David Maze Mar 31 '23 at 00:24
  • @DavidMaze, this sets a value depending on the arg is set or not. But what I'm trying to do is set the var comparing the arg with two predefined value. I tried with other `Parameter Expansion` patterns, but couldn't get that to work. – Masudur Rahman Mar 31 '23 at 16:02

2 Answers2

3

You should not think of your Dockerfile in the same way we think about shell scripts, with state. However, you can move your heavy lifting into a shell script to access desired functionality. Example Dockerfile:

FROM ubuntu

ARG TARGETARCH="arm64"
ENV TARGETARCH=$TARGETARCH

COPY init.sh /
RUN chmod +x /init.sh

CMD /init.sh

And then your init.sh could look like this:

#!/bin/bash 

if [ "$TARGETARCH" = "arm64" ]; then
    PROTOC_ARCH="aarch_64"
else
    PROTOC_ARCH="x86_64"
fi

echo PROTOC_ARCH is $PROTOC_ARCH and TARGETARCH is $TARGETARCH

Now you can access the env vars you expect and do conditional downloads, etc.

This pattern is quite common. You will find a lot of docker images using:

ENTRYPOINT ["docker-entrypoint.sh"]
code_monk
  • 9,451
  • 2
  • 42
  • 41
  • If I keep the download logic in the `entrypoint.sh` script, it will download the file and do other stuffs every time I run the docker image. – Masudur Rahman Mar 30 '23 at 20:02
  • True. So have a `setup.sh` script that is RUN and make your ENTRYPOINT something more relevant to your use case. – code_monk Mar 31 '23 at 22:03
1

Each RUN statement is run in a separate shell. That means that any environment variables you set are lost when the RUN statement finishes.

What you can do is run all your commands in a single RUN statement, like this

FROM ubuntu

ARG TARGETARCH="arm64"

ENV PROTOC_ARCH=""

RUN if [ "$TARGETARCH" = "arm64" ]; then \
       PROTOC_ARCH="aarch_64"; \
    else \
       PROTOC_ARCH="x86_64"; \
    fi && \
    apt-get update && apt-get install -y wget && \
    echo "PROTOC_ARCH=$PROTOC_ARCH" && \
    wget https://github.com/protocolbuffers/protobuf/releases/download/v22.2/protoc-22.2-linux-$PROTOC_ARCH.zip

Then PROTOC_ARCH will be available in the wget command, since it's not lost until the complete RUN statement is finished.

One thing to note is that at run-time, PROTOC_ARCH will be blank, since it's the value set in the ENV statement that is put in the image. AFAIK, there's no way to conditionally set a variable in an ENV statement.

Hans Kilian
  • 18,948
  • 1
  • 26
  • 35