Docker COPY files using glob pattern?

Fez Vrasta picture Fez Vrasta · Apr 20, 2018 · Viewed 33.8k times · Source

I have a monorepo managed by Yarn, I'd like to take advantage of the Docker cache layers to speed up my builds, to do so I'd like to first copy the package.json and yarn.lock files, run yarn install and then copy the rest of the files.

This is my repo structure:

packages/one/package.json
packages/one/index.js
packages/two/package.json
packages/two/index.js
package.json
yarn.lock

And this is the interested part of the Dockerfile:

COPY package.json .
COPY yarn.lock .
COPY packages/**/package.json ./
RUN yarn install --pure-lockfile
COPY . .

The problem is that the 3rd COPY command doesn't copy anything, how can I achieve the expected result?

Answer

ErikMD picture ErikMD · Apr 24, 2018

To follow-up @FezVrasta's comment to my first answer, if you can't possibly enumerate all the subdirectories at stake in the Dockerfile, but want to copy all the files in two steps to take advantage of Docker cache capabilities, you could try the following workaround:

  • Devise a wrapper script (say, in bash) that copies the required package.json files to a separate directory (say, .deps/) built with a similar hierarchy, then call docker build …
  • Adapt the Dockerfile to copy (and rename) the separate directory beforehand, and then call yarn install --pure-lockfile

All things put together, this could lead to the following files:

### ./build.bash ###
#!/bin/bash

tag=copy-example:latest

rm -f -r .deps  # optional, to be sure that there is
# no extraneous "package.json" from a previous build

find . -type d \( -path \*/.deps \) -prune -o \
  -type f \( -name "package.json" \) \
  -exec bash -c 'dest=".deps/$1" && \
    mkdir -p -- "$(dirname "$dest")" && \
    cp -av -- "$1" "$dest"' bash '{}' \;
# instead of mkdir + cp, you may also want to use
# rsync if it is available in your environment...

sudo docker build -t "$tag" .

and

### ./Dockerfile ###
FROM ...

WORKDIR /usr/src/app

# COPY package.json .  # subsumed by the following command
COPY .deps .
# and not "COPY .deps .deps", to avoid doing an extra "mv"
COPY yarn.lock .
RUN yarn install --pure-lockfile

COPY . .
# Notice that "COPY . ." will also copy the ".deps" folder; this is
# maybe a minor issue, but it could be avoided by passing more explicit
# paths than just "." (or by adapting the Dockerfile and the script and
# putting them in the parent folder of the Yarn application itself...)