Search code examples
docker.net-coredockerfile

Is there a more elegant way to copy specific files using Docker COPY to the working directory?


Attempting to create a container with microsoft/dotnet:2.1-aspnetcore-runtime. The .net core solution file has multiple projects nested underneath the solution, each with it's own .csproj file. I am attemping to create a more elegant COPY instruction for the sub-projects

The sample available here https://github.com/dotnet/dotnet-docker/tree/master/samples/aspnetapp has a solution file with only one .csproj so creates the Dockerfile thusly:

COPY *.sln .
COPY aspnetapp/*.csproj ./aspnetapp/
RUN dotnet restore

It works this way

COPY my_solution_folder/*.sln .
COPY my_solution_folder/project/*.csproj my_solution_folder/
COPY my_solution_folder/subproject_one/*.csproj subproject_one/
COPY my_solution_folder/subproject_two/*.csproj subproject_two/
COPY my_solution_folder/subproject_three/*.csproj subproject_three/

for a solution folder structure of:

my_solution_folder\my_solution.sln
my_solution_folder\project\my_solution.csproj
my_solution_folder\subproject_one\subproject_one.csproj
my_solution_folder\subproject_two\subproject_two.csproj
my_solution_folder\subproject_three\subproject_three.csproj

but this doesn't (was a random guess)

COPY my_solution_folder/*/*.csproj working_dir_folder/*/

Is there a more elegant solution?


Solution

  • 2021: with BuildKit, see ".NET package restore in Docker cached separately from build" from Palec.


    2018: Considering that wildcard are not well-supported by COPY (moby issue 15858), you can:

    • either experiment with adding .dockerignore files in the folder you don't want to copy (while excluding folders you do want): it is cumbersome
    • or, as shown here, make a tar of all the folders you want

    Here is an example, to be adapted in your case:

    find .. -name '*.csproj' -o -name 'Finomial.InternalServicesCore.sln' -o -name 'nuget.config' \
      | sort | tar cf dotnet-restore.tar -T - 2> /dev/null
    

    With a Dockerfile including:

    ADD docker/dotnet-restore.tar ./
    

    The idea is: the archive gets automatically expanded with ADD.


    The OP sturmstrike mentions in the comments "Optimising ASP.NET Core apps in Docker - avoiding manually copying csproj files (Part 2)" from Andrew Lock "Sock"

    The alternative solution actually uses the wildcard technique I previously dismissed, but with some assumptions about your project structure, a two-stage approach, and a bit of clever bash-work to work around the wildcard limitations.

    We take the flat list of csproj files, and move them back to their correct location, nested inside sub-folders of src.

    # Copy the main source project files
    COPY src/*/*.csproj ./  
    RUN for file in $(ls *.csproj); do mkdir -p src/${file%.*}/ && mv $file src/${file%.*}/; done
    

    L01nl suggests in the comments an alternative approach that doesn't require compression: "Optimising ASP.NET Core apps in Docker - avoiding manually copying csproj files", from Andrew Lock "Sock".

    FROM microsoft/aspnetcore-build:2.0.6-2.1.101 AS builder
    WORKDIR /sln
    
    COPY ./*.sln ./NuGet.config  ./
    
    # Copy the main source project files
    COPY src/*/*.csproj ./
    RUN for file in $(ls *.csproj); do mkdir -p src/${file%.*}/ && mv $file src/${file%.*}/; done
    
    # Copy the test project files
    COPY test/*/*.csproj ./
    RUN for file in $(ls *.csproj); do mkdir -p test/${file%.*}/ && mv $file test/${file%.*}/; done
    
    RUN dotnet restore
    
    # Remainder of build process
    

    This solution is much cleaner than my previous tar-based effort, as it doesn't require any external scripting, just standard docker COPY and RUN commands.
    It gets around the wildcard issue by copying across csproj files in the src directory first, moving them to their correct location, and then copying across the test project files.