Skip to content

bug: COPY does not take /. (slash-dot) into account #791

@thaJeztah

Description

@thaJeztah

Relates to moby/moby#38599

note: this is the same behavior as the classic builder, so we should discuss if we can change this without introducing a breaking change 😞

I think there's a bug in the copy behavior (both in classic builder, and in buildkit); it's come up a few times, and matching with the docker cp documentation;

SRC_PATH specifies a directory:

  • DEST_PATH does not exist
    • DEST_PATH is created as a directory and the contents of the source directory are copied into this directory
  • DEST_PATH exists and is a file
    • Error condition: cannot copy a directory to a file
  • DEST_PATH exists and is a directory
    • SRC_PATH does not end with /. (that is: slash followed by dot)
      • the source directory is copied into this directory
    • SRC_PATH does end with /. (that is: slash followed by dot)
      • the content of the source directory is copied into this directory

So without the trailing /., this line should result in dir itself being copied to /existingdir/dir; similar to cp:

Without trailing /.;

docker run --rm --workdir=/test alpine sh -c 'apk add --no-cache --quiet tree && mkdir -p src/dir/subdir dest/existingdir  && touch src/dir/subdir/nestedfile && cp -R src/dir dest/existingdir && cd /test/dest && tree'

.
└── existingdir
    └── dir
        └── subdir
            └── nestedfile

3 directories, 1 file

With trailing /.;

docker run --rm --workdir=/test alpine sh -c 'apk add --no-cache --quiet tree && mkdir -p src/dir/subdir dest/existingdir  && touch src/dir/subdir/nestedfile && cp -R src/dir/. dest/existingdir && cd /test/dest && tree'

.
└── existingdir
    └── subdir
        └── nestedfile

2 directories, 1 file

In a Dockerfile; without trailing /.;

DOCKER_BUILDKIT=1 docker build -<<EOF
FROM alpine AS source
RUN mkdir -p /dir/subdir && touch /dir/subdir/nestedfile

FROM alpine AS no_slash_dot
WORKDIR /dest
RUN apk add --no-cache --quiet tree
RUN mkdir existingdir
COPY --from=source /dir existingdir
RUN tree; exit 1
EOF

Produces;

.
└── existingdir
    └── subdir
        └── nestedfile

2 directories, 1 file

And with;

DOCKER_BUILDKIT=1 docker build -<<EOF
FROM alpine AS source
RUN mkdir -p /dir/subdir && touch /dir/subdir/nestedfile

FROM alpine AS slash_dot
WORKDIR /dest
RUN apk add --no-cache --quiet tree
RUN mkdir existingdir
COPY --from=source /dir/. existingdir
RUN tree; exit 1
EOF

Produces the same;

.
└── existingdir
    └── subdir
        └── nestedfile

2 directories, 1 file

The behavior should match the cp -R result

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions