-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathDockerfile
More file actions
344 lines (320 loc) · 15.3 KB
/
Dockerfile
File metadata and controls
344 lines (320 loc) · 15.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
# syntax=docker/dockerfile:1
# The syntax statement above is to make use of buildkit's build mounts for caching
##########################################################################################
# This Dockerfile makes use of BuildKit mode, which needs to be enabled client-side first:
#
# $ export DOCKER_BUILDKIT=1
#
# The following build args (e.g., `--build-arg`) are exposed to direct the usage of the
# PyPI Package Firewall within the image build itself:
#
# * PHYLUM_ORG: The organization to use (defaults to `Phylum`)
# * PHYLUM_GROUP: The group to use (defaults to `Phylum`)
# * PHYLUM_TOKEN: Phylum token from a user with access to the org/group
# * This is required and expected to be passed in as a build secret
#
# $ export PHYLUM_TOKEN=$(phylum auth token)
# $ docker build --tag phylum-ci --secret id=PHYLUM_TOKEN .
#
# This Dockerfile can be used to build the project's package within the image it creates.
# To do so, build the image WITHOUT any optional build args specified:
#
# $ export PHYLUM_TOKEN=$(phylum auth token)
# $ docker build --tag phylum-ci --secret id=PHYLUM_TOKEN .
#
# This Dockerfile can also be used in CI, as part of the release pipeline. The goal there
# is to ensure that the exact Python package that was built and released is the one that
# is installed while creating this image. Prerequisites are:
#
# * The package has already been built (e.g., `poetry build -vvv`)
# * There is exactly one wheel available to reference by glob expression
#
# To make use of this feature, build the image WITH these build args specified:
#
# $ export PHYLUM_TOKEN=$(phylum auth token)
# $ export PKG_SRC=dist/phylum-*.whl
# $ export PKG_NAME=phylum-*.whl
# $ docker build --tag phylum-ci --build-arg PKG_SRC --build-arg PKG_NAME \
# --secret id=PHYLUM_TOKEN .
#
# Another build arg is exposed to optionally specify the Phylum CLI version to install:
#
# $ export PHYLUM_TOKEN=$(phylum auth token)
# $ docker build --tag phylum-ci --build-arg CLI_VER=v4.8.0 --secret id=PHYLUM_TOKEN .
#
# The PHYLUM_API_URI build arg is exposed to optionally specify the URI of a Phylum API
# instance to use:
#
# $ export PHYLUM_TOKEN=$(phylum auth token)
# $ export PHYLUM_API_URI=https://api.staging.phylum.io
# $ docker build --tag phylum-ci --build-arg PHYLUM_API_URI --secret id=PHYLUM_TOKEN .
#
# Another build arg is exposed to optionally specify a GitHub Personal Access Token (PAT):
#
# $ export PHYLUM_TOKEN=$(phylum auth token)
# $ docker build --tag phylum-ci --build-arg GITHUB_TOKEN --secret id=PHYLUM_TOKEN .
#
# Providing a build argument like this (without a value) works when there is already an
# environment variable defined with the same name. Providing a GitHub PAT is useful to
# make authenticated requests and increase the API rate limit.
#
# To make use of BuildKit's inline layer caching feature, add the `BUILDKIT_INLINE_CACHE`
# build argument to any instance of building an image. Then, that image can be used
# locally or remotely (if it was pushed to a repository) to warm the build cache by using
# the `--cache-from` argument:
#
# $ export PHYLUM_TOKEN=$(phylum auth token)
# $ docker build --tag phylumio/phylum-ci:cache --build-arg BUILDKIT_INLINE_CACHE=1 \
# --secret id=PHYLUM_TOKEN .
# $ docker push phylumio/phylum-ci:cache && docker image rm phylumio/phylum-ci:cache
# $ docker build --tag phylumio/phylum-ci:faster --cache-from phylumio/phylum-ci:cache \
# --secret id=PHYLUM_TOKEN .
#
# There is no ENTRYPOINT in this Dockerfile by design. That way, it is possible to provide
# unquoted extra parameters to run arbitrary commands in the context of the container:
#
# $ docker run --rm phylumio/phylum-ci:latest ls -alh /
#
# However, there may be cases where an entrypoint is needed. One is provided and placed in
# a directory that will be included in the final layer and also known to be part of the
# $PATH. To make use of it, add the `--entrypoint` option to a docker run command,
# specifying the `entrypoint.sh` script, providing extra parameters as a *quoted* string:
#
# $ docker run --rm --entrypoint entrypoint.sh phylumio/phylum-ci:latest "ls -alh /"
#
# Images built from this Dockerfile can be tested for basic functionality:
#
# $ export PHYLUM_TOKEN=$(phylum auth token)
# $ docker build --tag phylum-ci --secret id=PHYLUM_TOKEN .
# $ scripts/docker_tests.sh --image phylum-ci
##########################################################################################
FROM python:3.13-slim-bookworm AS builder
# PKG_SRC is the path to a built distribution/wheel and PKG_NAME is the name of the built
# distribution/wheel. Both can optionally be specified in glob form. When not defined,
# the values will default to the root of the package (i.e., `pyproject.toml` path).
ARG PKG_SRC
ARG PKG_NAME
# Package Firewall Variables:
#
# PHYLUM_ORG and PHYLUM_GROUP specify the org and group, respectively, of the account
# to be used when accessing the Package Firewall. Both default to `Phylum`.
ARG PHYLUM_ORG="Phylum"
ARG PHYLUM_GROUP="Phylum"
# PHYLUM_TOKEN is required and passed as a build secret that can be used to specify the
# Phylum token for a user with access to the org/group used for the Package Firewall.
#
# $ export PHYLUM_TOKEN=$(phylum auth token)
# $ docker build --tag phylum-ci --secret id=PHYLUM_TOKEN .
ARG PHYLUM_TOKEN
ENV APP_PATH="/app"
ENV POETRY_VENV="${APP_PATH}/.venv"
ENV POETRY_PATH="${POETRY_VENV}/bin/poetry"
ENV PHYLUM_VENV="/opt/venv"
ENV PHYLUM_VENV_PIP="${PHYLUM_VENV}/bin/pip"
ENV PIP_NO_COMPILE=1
ENV PIP_DISABLE_PIP_VERSION_CHECK=1
ENV POETRY_VERSION="2.2.1"
WORKDIR ${APP_PATH}
RUN python -m venv ${PHYLUM_VENV}
# Isolate the use of the `PHYLUM_TOKEN` secret to a single RUN command so as not
# to leak the value in logs with `set -eux` command
RUN --mount=type=secret,id=PHYLUM_TOKEN,env=PHYLUM_TOKEN,required=true \
${PHYLUM_VENV_PIP} config set global.index-url \
"https://${PHYLUM_ORG}%2F${PHYLUM_GROUP}:${PHYLUM_TOKEN}@pypi.phylum.io/simple/"
RUN ${PHYLUM_VENV_PIP} install --no-cache-dir --upgrade pip setuptools
RUN set -eux; \
python -m venv ${POETRY_VENV}; \
${POETRY_VENV}/bin/pip install --no-cache-dir --upgrade pip setuptools; \
${POETRY_VENV}/bin/pip install --no-cache-dir poetry==${POETRY_VERSION} poetry-plugin-export
# Copy the bare minimum needed for specifying dependencies.
# This will enable better layer caching and faster builds when iterating locally.
# `--without-hashes` is used to ensure the `pip` cache mount is used for packages that
# would otherwise only match the sdist and therefore have to be built for every run.
# `--without-urls` is used to ensure the globally set pip index-url is used when installing
# these dependencies in a later step, to make use of the PyPI Package Firewall.
# References:
# * https://pythonspeed.com/articles/pipenv-docker/
# * https://hub.docker.com/r/docker/dockerfile
COPY pyproject.toml poetry.lock ./
RUN ${POETRY_PATH} export --without-hashes --without-urls --format requirements.txt --output requirements.txt
# Cache the pip installed dependencies for faster builds when iterating locally.
# NOTE: This `--mount` feature requires BUILDKIT to be used
RUN --mount=type=cache,id=pip,target=/root/.cache/pip \
set -eux; \
${PHYLUM_VENV_PIP} cache info; \
${PHYLUM_VENV_PIP} cache list; \
${PHYLUM_VENV_PIP} install -r requirements.txt pipenv poetry==${POETRY_VERSION}
COPY "${PKG_SRC:-.}" .
RUN ${PHYLUM_VENV_PIP} install --no-cache-dir ${PKG_NAME:-.}
RUN find ${PHYLUM_VENV} -type f -name '*.pyc' -delete
# Place the ENTRYPOINT alternative script in a directory included
# in the final layer and also known to be part of the $PATH
COPY entrypoint.sh ${PHYLUM_VENV}/bin/
FROM python:3.13-slim-bookworm
# CLI_VER specifies the Phylum CLI version to install in the image.
# Values should be provided in a format acceptable to the `phylum-init` script.
# When not defined, the value will default to `latest`.
ARG CLI_VER
# PHYLUM_API_URI is an optional build argument that can be used to specify
# the URI of a Phylum API instance to use.
ARG PHYLUM_API_URI
# GITHUB_TOKEN is an optional build argument that can be used to provide a
# GitHub Personal Access Token (PAT) in order to make authenticated requests
# and therefore increase the API rate limit.
ARG GITHUB_TOKEN
# Ref: https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope
ARG TARGETOS
ARG TARGETARCH
LABEL maintainer="Veracode, Inc. <dl-phylum-engineering@veracode.com>"
LABEL org.opencontainers.image.source="https://github.com/phylum-dev/phylum-ci"
ENV PHYLUM_VENV="/opt/venv"
# Some tools get installed and used based on a home directory. This can cause trouble
# when using a container started from this image with a UID:GID that does not map to
# a user/group account in the container. Examples include:
#
# * The installation directory is `/root` and the container user has no access
# * The tool relies on the existence of a $HOME directory, which may not be true
# * https://medium.com/redbubble/running-a-docker-container-as-a-non-root-user-7d2e00f8ee15
#
# The following tools are susceptible to this issue and therefore have been provided
# with explicit install/home directories that allow them to be globally accessible.
#
# Specify a common, globally accessible directory to use instead of $HOME
ENV INSTALL_DIR="/usr/local"
# Phylum, pip, and any other tool that supports the XDG spec
# Ref: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
ENV XDG_DATA_HOME="${INSTALL_DIR}/share"
ENV XDG_CONFIG_HOME="${INSTALL_DIR}/.config"
ENV XDG_STATE_HOME="${INSTALL_DIR}/state"
ENV XDG_CACHE_HOME="${INSTALL_DIR}/.cache"
# Node and npm
# Ref: https://github.com/tj/n
ENV N_PREFIX="${INSTALL_DIR}/n"
# Corepack, Yarn, and pnpm
# Ref: https://github.com/nodejs/corepack#environment-variables
ENV COREPACK_HOME="${INSTALL_DIR}/corepack"
# Rust, Cargo, and rustup
# Ref: https://rust-lang.github.io/rustup/installation/index.html#choosing-where-to-install
ENV RUSTUP_HOME="${INSTALL_DIR}/rustup"
ENV CARGO_HOME="${INSTALL_DIR}/cargo"
# Gradle
# Ref: https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_environment_variables
ENV GRADLE_HOME="${INSTALL_DIR}/gradle"
ENV GRADLE_USER_HOME="${GRADLE_HOME}/.gradle"
# Dotnet
# Ref: https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-environment-variables
ENV DOTNET_ROOT="/usr/share/dotnet"
ENV DOTNET_CLI_HOME="${XDG_DATA_HOME}"
# Go
# Ref: https://go.dev/doc/install/source#environment
ENV GOROOT="${INSTALL_DIR}/go"
ENV GOPATH="${XDG_DATA_HOME}/go"
ENV PATH=$PATH:${PHYLUM_VENV}/bin:${N_PREFIX}/bin:${GRADLE_HOME}/bin:${CARGO_HOME}/bin:${GOROOT}/bin
ENV PYTHONDONTWRITEBYTECODE=1
# Ref: https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-environment-variables
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
ENV DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
ENV DOTNET_NOLOGO=1
# Copy only Python packages to limit the image size. This includes the `phylum` package with it's
# `phylum-ci` and `phylum-init` entry points, plus the `pip`, `pipenv`, and `poetry` required tools.
COPY --from=builder ${PHYLUM_VENV} ${PHYLUM_VENV}
# Specify the shell options here, based on the use of pipelines.
# Ref: https://github.com/hadolint/hadolint/wiki/DL4006
SHELL ["/bin/bash", "-euxo", "pipefail", "-c"]
RUN \
# Install prerequisites and package manager versions for `npm`, `bundle`, and `mvn` tools
apt-get update; \
apt-get upgrade --yes; \
apt-get install --yes --no-install-recommends \
git \
git-lfs \
bundler \
maven \
default-jdk-headless \
procps \
curl \
unzip \
jq \
; \
# Make ENTRYPOINT alternative script available
chmod +x "${PHYLUM_VENV}/bin/entrypoint.sh"; \
#
# Create a shell function instead of an alias for `curl`, with secure and common options
# Ref: https://github.com/koalaman/shellcheck/wiki/SC2262
curls() { curl --proto "=https" --tlsv1.2 -sSfL "$@"; }; \
#
# Install Phylum CLI
phylum-init -vvv --phylum-release "${CLI_VER:-latest}" --global-install; \
#
# Install `node` and `npm` with `n`
# Ref: https://github.com/tj/n
curls -o n.sh https://raw.githubusercontent.com/tj/n/master/bin/n; \
chmod +x n.sh && ./n.sh install lts; \
npm config set --global engine-strict=true; \
./n.sh rm lts && rm n.sh; \
#
# Install and enable `corepack` with cached instances of the latest major versions of `yarn` and `pnpm` tools
npm install --global corepack@latest; \
corepack pack yarn@stable pnpm@latest; \
corepack enable yarn pnpm; \
#
# Manual install of `gradle`
GRADLE_VERSION=$(curls https://services.gradle.org/versions/current | jq -r '.version'); \
GRADLE_DL="https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip"; \
GRADLE_DL_SHA256=$(curls "${GRADLE_DL}.sha256"); \
curls -o gradle.zip "${GRADLE_DL}"; \
printf "%s *gradle.zip" "${GRADLE_DL_SHA256}" | sha256sum -c -; \
mkdir "${GRADLE_HOME}"; \
unzip -d "${GRADLE_HOME}" gradle.zip; \
mv "${GRADLE_HOME}/gradle-${GRADLE_VERSION}"/* "${GRADLE_HOME}/"; \
rm gradle.zip && rmdir "${GRADLE_HOME}/gradle-${GRADLE_VERSION}/"; \
mkdir --mode=777 "${GRADLE_USER_HOME}"; \
#
# Manual install of `go`
# Ref: https://pkg.go.dev/golang.org/x/website/internal/dl
GO_DL_URL="https://go.dev/dl/?mode=json"; \
GO_DL_REL=$(curls "${GO_DL_URL}" | jq '.[0].files[] | select(.os==env.TARGETOS and .arch==env.TARGETARCH)'); \
GO_DL_FILENAME=$(echo "${GO_DL_REL}" | jq -r '.filename'); \
GO_DL_SHA256=$(echo "${GO_DL_REL}" | jq -r '.sha256'); \
curls -o go.tgz "https://go.dev/dl/${GO_DL_FILENAME}"; \
printf "%s *go.tgz" "${GO_DL_SHA256}" | sha256sum -c -; \
rm -rf "${GOROOT}"; \
tar -C "${INSTALL_DIR}" -xzf go.tgz; \
rm go.tgz; \
#
# Manual install of Rust to get `cargo` tool
curls https://sh.rustup.rs | sh -s -- -v -y --default-toolchain stable --profile minimal; \
#
# Install .NET SDK to get `dotnet` tool
# Ref: https://github.com/dotnet/core/tree/main/release-notes
DOTNET_SDK_LATEST_CHANNEL_VER=$( \
curls https://raw.githubusercontent.com/dotnet/core/main/release-notes/releases-index.json | \
jq -r '[."releases-index"[] | select(."support-phase"=="active")] | first."channel-version"' \
); \
DEB_MAJ_VER=$(cut -d "." -f1 /etc/debian_version); \
curls -o ms-prod.deb "https://packages.microsoft.com/config/debian/${DEB_MAJ_VER}/packages-microsoft-prod.deb"; \
dpkg --install ms-prod.deb; \
rm ms-prod.deb; \
apt-get update; \
apt-get install --yes --no-install-recommends "dotnet-sdk-${DOTNET_SDK_LATEST_CHANNEL_VER}"; \
#
# Create a git config file in a location accessible for $HOME-less users
# Ref: https://git-scm.com/docs/git-config#FILES
mkdir -vp "${XDG_CONFIG_HOME}/git" && touch "${XDG_CONFIG_HOME}/git/config"; \
#
# Ensure non-root users have necessary permissions
mkdir -vp "${XDG_DATA_HOME}" "${XDG_CONFIG_HOME}" "${XDG_STATE_HOME}" "${XDG_CACHE_HOME}"; \
chmod -vR 777 "${XDG_DATA_HOME}" "${XDG_CONFIG_HOME}" "${XDG_STATE_HOME}" "${XDG_CACHE_HOME}"; \
chmod -v 666 "${COREPACK_HOME}/lastKnownGood.json"; \
#
# Final cleanup
apt-get remove --yes --auto-remove \
curl \
unzip \
jq \
; \
apt-get purge --yes --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
rm -rf /var/lib/apt/lists/*; \
rm -rf /tmp/*; \
find / -type f -name '*.pyc' -delete;
CMD ["phylum-ci"]