dockerdockerfile

Conditional COPY/ADD in Dockerfile?


Inside of my Dockerfiles I would like to COPY a file into my image if it exists, the requirements.txt file for pip seems like a good candidate but how would this be achieved?

COPY (requirements.txt if test -e requirements.txt; fi) /destination
...
RUN  if test -e requirements.txt; then pip install -r requirements.txt; fi

or

if test -e requirements.txt; then
    COPY requiements.txt /destination;
fi
RUN  if test -e requirements.txt; then pip install -r requirements.txt; fi

Solution

  • 2021+, from this answer, using glob pattern actually Go filepath patterns, Docker COPY will not fail if it won't locate any valid source

    COPY requiements.tx[t] /destination
    

    2015: This isn't currently supported (as I suspect it would lead to a non-reproducible image, since the same Dockerfile would copy or not the file, depending on its existence).

    This is still requested, in issue 13045, using wildcards: "COPY foo/* bar/" not work if no file in foo" (May 2015).
    It won't be implemented for now (July 2015) in Docker, but another build tool like bocker could support this.


    2021:

    COPY source/. /source/ works for me (i.e. copies directory when empty or not, as in "Copy directory into docker build no matter if empty or not - fails on "COPY failed: no source files were specified"")

    2022

    Here is my suggestion:

    # syntax=docker/dockerfile:1.2
    
    RUN --mount=type=bind,source=jars,target=/build/jars \
     find /build/jars -type f -name '*.jar' -maxdepth 1  -print0 \
     | xargs -0 --no-run-if-empty --replace=source cp --force source >"${INSTALL_PATH}/modules/"
    

    That works around:

    COPY jars/*.jar "${INSTALL_PATH}/modules/"
    

    But copies no *.jar if none is found, without throwing an error.

    Q2 2024, regarding the 2022 solution, x-yuri adds in the comments:

    The idea is clear, but your 2022 solution doesn't work. b.sh.
    Solution that works. c.sh is like b.sh but w/o xargs.

    set -eux
    rm -rf a
    mkdir a
    cd a
    
    mkdir src
    touch src/a
    # touch src/b.txt
    # touch src/c.txt
    cat <<\EOF >Dockerfile
    FROM debian:bookworm-slim
    RUN mkdir dst
    RUN --mount=type=bind,target=/mnt \
      find /mnt/src -maxdepth 1 -type f -name '*.txt' -exec cp -t dst {} +
    EOF
    docker build -t i --progress=plain .
    docker run --rm i ls -A dst