
Installing application dependencies
The next step is to install application dependencies, which, as you learned in the previous chapter, means installing packages defined in the src/requirements.txt and src/requirements_test.txt files:
# Test stage
FROM alpine AS test
LABEL application=todobackend
# Install basic utilities
RUN apk add --no-cache bash git
# Install build dependencies
RUN apk add --no-cache gcc python3-dev libffi-dev musl-dev linux-headers mariadb-dev
RUN pip3 install wheel
# Copy requirements
COPY /src/requirements* /build/
WORKDIR /build
# Build and install requirements
RUN pip3 wheel -r requirements_test.txt --no-cache-dir --no-input
RUN pip3 install -r requirements_test.txt -f /build --no-index --no-cache-dir
You first use the COPY directive to copy the src/requirements.txt and src/requirements_test.txt files to a folder in the /build container, which you then specify as the working directory via the WORKDIR directive. Note that /src/requirements.txt is not a physical path on your Docker client - it is a path within the Docker build context, which is a configurable location on your Docker client file system that you specify whenever you execute a build. To ensure all relevant application source code files are available for the Docker build process, a common practice is to set the root of your application repository as the build context, so in the example above /src/requirements.txt refers to <path-to-repository>/src/requirements.txt on your Docker client.
Next, you use the pip3 wheel command to build Python wheels into the /build working directory for all of the base application and test dependencies, using the --no-cache-dir flag to avoid bloating our image and the --no-input flag to disable prompting for user confirmations. Finally, you install the previously built wheels into the container using the pip3 install command, using the --no-index flag to instruct pip not to attempt to download any packages from the internet, and instead install all packages from the /build folder as specified by the -f flag.
This approach may seem a little strange, however, it is based upon the principle that you should only build your application dependencies once as installable packages, and then install the built dependencies as required. Later on, we will install the same dependencies into the release image, ensuring that your release image accurately reflects the exact set of dependences your application was tested and built against.