Diff of /Dockerfile [000000] .. [e988c2]

Switch to unified view

a b/Dockerfile
1
# syntax=docker/dockerfile:1.2
2
#################################################
3
#
4
# Initial ehrQL layer with just system dependencies installed.
5
#
6
# hadolint ignore=DL3007
7
FROM ghcr.io/opensafely-core/base-action:latest as ehrql-dependencies
8
9
10
# setup default env vars for all images
11
# ACTION_EXEC sets the default executable for the entrypoint in the base-action image
12
ENV VIRTUAL_ENV=/opt/venv/ \
13
    PYTHONPATH=/app \
14
    PATH="/opt/venv/bin:/opt/mssql-tools/bin:$PATH" \
15
    ACTION_EXEC=ehrql \
16
    PYTHONUNBUFFERED=True \
17
    PYTHONDONTWRITEBYTECODE=1 \
18
    PYTHONHASHSEED=0
19
20
RUN mkdir /workspace
21
WORKDIR /workspace
22
23
# We are going to use an apt cache on the host, so disable the default debian
24
# docker clean up that deletes that cache on every apt install
25
RUN rm -f /etc/apt/apt.conf.d/docker-clean
26
27
# Add Microsoft package archive for installing MSSQL tooling
28
# Add deadsnakes PPA for installing new Python versions
29
RUN --mount=type=cache,target=/var/cache/apt \
30
    /usr/lib/apt/apt-helper download-file \
31
        "https://packages.microsoft.com/keys/microsoft.asc" \
32
        /etc/apt/trusted.gpg.d/microsoft.asc && \
33
    /usr/lib/apt/apt-helper download-file \
34
      "https://packages.microsoft.com/config/ubuntu/20.04/prod.list" \
35
      /etc/apt/sources.list.d/mssql-release.list && \
36
    echo "deb https://ppa.launchpadcontent.net/deadsnakes/ppa/ubuntu focal main" \
37
        > /etc/apt/sources.list.d/deadsnakes-ppa.list && \
38
    /usr/lib/apt/apt-helper download-file \
39
        'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0xf23c5a6cf475977595c89f51ba6932366a755776' \
40
        /etc/apt/trusted.gpg.d/deadsnakes.asc
41
42
# Install root dependencies, including Python
43
COPY dependencies.txt /root/dependencies.txt
44
# use space efficient utility from base image
45
RUN --mount=type=cache,target=/var/cache/apt \
46
    /usr/bin/env ACCEPT_EULA=Y /root/docker-apt-install.sh /root/dependencies.txt
47
48
#################################################
49
#
50
# Next, use the dependencies image to create an image to build dependencies
51
FROM ehrql-dependencies as ehrql-builder
52
53
# install build time dependencies
54
COPY build-dependencies.txt /root/build-dependencies.txt
55
RUN /root/docker-apt-install.sh /root/build-dependencies.txt
56
57
58
# install everything in venv for isolation from system python libraries
59
# hadolint ignore=DL3013,DL3042
60
RUN --mount=type=cache,target=/root/.cache \
61
    /usr/bin/python3.11 -m venv /opt/venv && \
62
    /opt/venv/bin/python -m pip install -U pip setuptools wheel
63
64
COPY requirements.prod.txt /root/requirements.prod.txt
65
# hadolint ignore=DL3042
66
RUN --mount=type=cache,target=/root/.cache python -m pip install -r /root/requirements.prod.txt
67
68
# WARNING clever/ugly python packaging hacks alert
69
#
70
# Borrowed (with modifications) from:
71
# https://github.com/opensafely-core/cohort-extractor/blob/669e2d5d2e/Dockerfile#L54-L84
72
#
73
# We could just do `COPY . /app` and then `pip install /app`. However, this is
74
# not ideal for a number of reasons:
75
#
76
# 1) Any changes to the app files will invalidate this and all subsequent
77
#    layers, causing them to need rebuilding. This would mean basically
78
#    reinstalling dev dependencies every time.
79
#
80
# 2) We want to use the pinned versions of dependencies in
81
#    requirements.prod.txt rather than the unpinned versions in setup.py.
82
#
83
# 3) We want for developers be able to mount /app with their code and it Just
84
#    Works, without reinstalling anything.
85
#
86
# So, we do the following:
87
#
88
# 1) Copy a stripped down version of the pyproject.toml file, and install an
89
#    empty package from it alone (a test ensured the minimal version stays in
90
#    sync with the full version)
91
#
92
# 2) We install it without deps, as they've already been installed.
93
#
94
# 3) We have set PYTHONPATH=/app, so that code copied or mounted into /app will
95
#    be used automatically.
96
#
97
# Note: we only really need to install it at all to use setuptools entrypoints.
98
RUN mkdir /app
99
COPY pyproject.minimal.toml /app/pyproject.toml
100
# hadolint ignore=DL3042
101
RUN --mount=type=cache,target=/root/.cache \
102
  /opt/venv/bin/python -m pip install --no-deps /app
103
104
105
################################################
106
#
107
# A base image with the including the prepared venv and metadata.
108
FROM ehrql-dependencies as ehrql-base
109
110
# Some static metadata for this specific image, as defined by:
111
# https://github.com/opencontainers/image-spec/blob/master/annotations.md#pre-defined-annotation-keys
112
# The org.opensafely.action label is used by the jobrunner to indicate this is
113
# an approved action image to run.
114
LABEL org.opencontainers.image.title="ehrql" \
115
      org.opencontainers.image.description="ehrQL action for opensafely.org" \
116
      org.opencontainers.image.source="https://github.com/opensafely-core/ehrql" \
117
      org.opensafely.action="ehrql"
118
119
COPY --from=ehrql-builder /opt/venv /opt/venv
120
121
122
################################################
123
#
124
# Build the actual production image from the base
125
FROM ehrql-base as ehrql
126
127
# Copy app code. This will be automatically picked up by the virtualenv as per
128
# comment above
129
COPY ehrql /app/ehrql
130
RUN python -m compileall /app/ehrql
131
COPY bin /app/bin
132
133
# The following build details will change.
134
# These are the last step to make better use of Docker's build cache,
135
# avoiding rebuilding image layers unnecessarily.
136
ARG BUILD_DATE=unknown
137
LABEL org.opencontainers.image.created=$BUILD_DATE
138
ARG GITREF=unknown
139
LABEL org.opencontainers.image.revision=$GITREF