Repository navigation
Expand file tree
/
Copy pathDockerfile
More file actions
953 lines (854 loc) · 43.7 KB
/
Copy pathDockerfile
File metadata and controls
953 lines (854 loc) · 43.7 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
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
# This Dockerfile defines a number of images, namely:
#
# respect-current-minimum:
# A common base image, containing the minimum needed for running our code.
# In the future this image might be merged with `respect-build-common` if we
# base `respect-base-image` on a different (more lightweight) image.
#
# respect-base-image:
# The image we use in `bazel` as our base image. This is the one we ship and
# move around in our cluster.
#
# respect-build-common:
# Adds all the tooling necessary to actually build and deploy respect. This
# serves as the base image for our development.
#
# codespace-workstation:
# The image extends `respect-build-common` and adds all the github specific
# modifications and hooks that allow us to use it with codespaces.
#
# reboot-envoy:
# Used when we run Envoy in a Docker container. Based on the upstream
# istio/proxyv2 image, with some minor extensions.
# The following ARGs must be defined before the first FROM, since they'll
# parameterize a FROM.
# Latest version as of 2026-09-09. Keep in sync with the Istio version
# that our Kubernetes clusters are deployed with, so that the
# `istioctl` in this image speaks to a control plane of its own
# version.
ARG ISTIO_VERSION=1.30.4
# This version should match `ENVOY_VERSION` in `reboot/settings.py`
# and `reboot/containers/reboot-base/Dockerfile`. Istio 1.30.4 builds
# its proxy from Envoy `1.38.4-dev`, so `1.38.4` is the released Envoy
# patch that most closely matches the mesh proxies.
ARG ENVOY_VERSION=1.38.4
# Clang version used for C++ compilation on the host and in the
# manylinux-builder container.
ARG CLANG_VERSION=20
# Raw `curl` and `wget` downloads in this file retry transient
# failures: `curl --retry 5 --retry-all-errors -f` (plain `--retry`
# does not retry connection resets, hence `--retry-all-errors`, and
# `-f`/`--fail` turns an HTTP error status into a failed download
# rather than writing the error body to the output file) and `wget
# --tries=5 --waitretry=10 --retry-connrefused`. Installer scripts
# are downloaded to a temporary file and executed from it rather than
# piped straight into an interpreter, because on a retry after a
# mid-body failure curl re-sends the whole body into the
# already-written pipe, corrupting the stream. Pipes into key-material
# consumers like `apt-key` and `gpg --dearmor` keep the pipe: their
# input is validated cryptographically, so a torn stream fails the
# build loudly.
###############################################################################
# Use a specific ubuntu version so we're not surprised by silent changes to gcc
# versions (and thus C++ feature support). We additionally care about the `glibc`
# version which is in use, as it becomes a minimum version in our `manylinux`
# published wheels.
# Jammy comes with:
# * gcc 11
# * glibc 2.35
FROM ubuntu:jammy-20251001 AS respect-current-minimum
ARG TARGETARCH
# Ubuntu Jammy does not have Python installed, so we use the ppa to install it.
ARG PYTHON_VERSION=3.10
# Transient DNS and connection failures on CI runners make single-shot
# `apt` fetches flaky, so have `apt` retry each acquire a few times.
# Every stage built from this one inherits the setting.
RUN echo 'Acquire::Retries "5";' > /etc/apt/apt.conf.d/80-retries
# Install tzdata package to provide timezone files that tzlocal depends on.
# Without this, /etc/localtime symlink to /usr/share/zoneinfo/Etc/UTC breaks,
# causing "tzlocal() does not support non-zoneinfo timezones" errors.
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
curl gpg gpg-agent software-properties-common tzdata \
&& rm -rf /var/lib/apt/lists/*
RUN add-apt-repository ppa:deadsnakes/ppa \
&& apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
python${PYTHON_VERSION} \
python${PYTHON_VERSION}-dev \
python${PYTHON_VERSION}-venv \
&& rm -rf /var/lib/apt/lists/*
RUN update-alternatives --install /usr/bin/python usr-bin-python /usr/bin/python${PYTHON_VERSION} 3 \
&& update-alternatives --install /usr/local/bin/python usr-local-bin-python /usr/bin/python${PYTHON_VERSION} 3 \
&& update-alternatives --install /usr/local/bin/python3 usr-local-bin-python3 /usr/bin/python${PYTHON_VERSION} 3 \
# Confirm that python3 is installed and that it has the expected version.
&& python3 --version | grep -Eq "^Python ${PYTHON_VERSION}.*" \
# And that "python" (aka python3) is installed and has the expected version.
&& python --version | grep -Eq "^Python ${PYTHON_VERSION}.*"
# Ensure pip is installed and up to date.
RUN curl --retry 5 --retry-all-errors -fsS https://bootstrap.pypa.io/get-pip.py \
-o /tmp/get-pip.py \
&& python /tmp/get-pip.py \
&& rm /tmp/get-pip.py \
&& update-alternatives --install /usr/local/bin/pip pip /usr/local/bin/pip3 3
ENV TINI_VERSION=v0.19.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-${TARGETARCH} /usr/bin/tini
RUN chmod +x /usr/bin/tini
# Installs `wget` and `curl` to aid debugging and to install other
# tools, with ca-certificates to make https calls. The `tini` package is
# installed to help with signal handling in the container. Git is used by `rbt` and
# must be on the PATH.
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
ca-certificates \
wget \
curl \
git \
&& rm -rf /var/lib/apt/lists/*
# Install `grpcurl`, which is very helpful for debugging. See:
# https://github.com/fullstorydev/grpcurl
ARG GRPCURL_VERSION=1.9.4
RUN if [ "${TARGETARCH}" = "amd64" ]; then \
wget --tries=5 --waitretry=10 --retry-connrefused https://github.com/fullstorydev/grpcurl/releases/download/v${GRPCURL_VERSION}/grpcurl_${GRPCURL_VERSION}_linux_x86_64.tar.gz \
&& tar -xvf ./grpcurl_${GRPCURL_VERSION}_linux_x86_64.tar.gz grpcurl \
&& rm ./grpcurl_${GRPCURL_VERSION}_linux_x86_64.tar.gz \
&& chmod +x grpcurl \
&& mv ./grpcurl /usr/local/bin/grpcurl; \
fi
ENTRYPOINT ["/usr/bin/tini", "--"]
###############################################################################
# Create an image that can be used by bazel as the base image.
FROM respect-current-minimum AS respect-base-image
# TODO: The `respect-base-image` is used and shipped a lot. We might want to
# try and slim it down as it dramatically reduces load times in our cluster.
# Using an alpine image did not work, but the difference between 10s of MBs vs
# 100s of MB is significant! Especially as images are currently aggressively
# copied around our cluster (3 containers) and for every images (typically ~7).
# There is no reason that `respect-base-image` is based on
# `respect-current-minimum` or a fullblown ubuntu image. There is a lot in
# there we don't need.
#
# NOTE: We can not use `scratch` without at at least adding an empty file. If
# we don't add anything to scratch, docker will complain that the image is
# (indeed) empty.
#
# NOTE: We can not use python:x.y.z-alpine as something or someone is looking
# for python in /usr/bin/python (and similar for python3). This can be
# symlinked away but isn't a sufficient fix. e.g., RUN ln -s
# /usr/local/bin/python /usr/bin/python RUN ln -s /usr/local/bin/python3
# /usr/bin/python3
#
# NOTE: Alpine-bazel combo seems to not provide sufficient libraries out of the
# gate. The earliest we blow up is when trying to import psutils and not
# finding it. Maybe this is misconfiguration on our side, but I will not spend
# more time investigating now.
###############################################################################
# Add build tools necessary for building respect.
FROM respect-current-minimum AS respect-build-common
ARG ISTIO_VERSION
ARG TARGETARCH
ARG CLANG_VERSION
ARG ENVOY_VERSION
# Docker version format is Ubuntu-style:
# [epoch]:[version]-[revision]-[ubuntu-suffix]
ARG DOCKER_VERSION=5:29.8.0-1~ubuntu.22.04~jammy
RUN apt-get update \
# Install various prerequisite packages need for building as well as
# packages that aid developing and debugging.
#
# As the list/version of packages is not changing too often, we optimize
# for image layer size and run all `apt` related commands (that includes
# the invocation of `llvm.sh`) in one `RUN` statement.
#
# If you are adding packages to the list below, please add a comment here
# as well.
#
# Additional mandatory packages we install:
# * build-essential: get gcc and std headers.
# * ca-certificates: dependency for curl to make https calls.
# * curl: not strictly needed (while-false: I think) outside of image
# building but small (~100kb) and useful for debugging. Might be used
# internally by bazel to fetch `http_archives`.
# * gnupg: for image and package signing.
# * lsb-release: to allow install scripts/(debugging )developers to figure
# out what system they are on.
# * make: required to build cc_image targets (alexmc: I think!).
# * openssh-client: much needed dependency of `git`.
# * python3-dev: python source code needed for building grpc bindings.
# * python3-distutils: for building and compiling python packages.
# * wget: see `curl`.
# * jq: used by our tests to parse JSON output from `kubectl`.
#
# Additional optional packages we install:
# * gdb: debugger
# * htop: replacement for top.
# * iputils-ping: to get `ping`.
# * vim: it's vi improved!
#
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
# Mandatory packages:
build-essential \
ca-certificates \
curl \
gnupg \
gnupg-agent \
apt-transport-https \
lsb-release \
make \
openssh-client \
python3-dev \
python3-distutils \
software-properties-common \
sudo \
wget \
jq \
# For our build of `lzma` to work, libraries provided by `gettext` are needed.
gettext \
# Optional packages:
bash-completion \
gdb \
htop \
iputils-ping \
vim \
zsh \
# Install clang. Instructions:
# https://apt.llvm.org/
&& wget --tries=5 --waitretry=10 --retry-connrefused -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - \
&& add-apt-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-${CLANG_VERSION} main" \
&& apt-get update \
&& apt install -y clang-${CLANG_VERSION} \
# Make clang mean clang-xx.
&& ln -s /usr/bin/clang-${CLANG_VERSION} /usr/bin/clang \
# Install clang-format and symlink to `clang-format`.
&& DEBIAN_FRONTEND=noninteractive apt-get install -y clang-format-${CLANG_VERSION} \
&& ln -s /usr/bin/clang-format-${CLANG_VERSION} /usr/bin/clang-format \
# As of ~2023 we also need to explicitly include
# 'libclang-rt-${CLANG_VERSION}-dev' so that we can
# build with '--config=asan'.
&& DEBIAN_FRONTEND=noninteractive apt-get install -y libclang-rt-${CLANG_VERSION}-dev \
# Install Docker from the official Docker repository. Instructions:
# https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository
&& install -m 0755 -d /etc/apt/keyrings \
&& curl --retry 5 --retry-all-errors -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc \
&& chmod a+r /etc/apt/keyrings/docker.asc \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null \
&& apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y docker-ce=${DOCKER_VERSION} docker-ce-cli=${DOCKER_VERSION} containerd.io docker-buildx-plugin docker-compose-plugin \
# Cleanup.
&& apt-get purge --auto-remove -y \
&& rm -rf /var/lib/apt/lists/*
# Install Bazel.
ARG BAZELISK_VERSION=v1.29.0
RUN wget --tries=5 --waitretry=10 --retry-connrefused -O /usr/local/bin/bazel https://github.com/bazelbuild/bazelisk/releases/download/${BAZELISK_VERSION}/bazelisk-linux-${TARGETARCH} \
&& chmod +x /usr/local/bin/bazel
# Install k3d.io which we'll use to run integration tests.
# See https://k3d.io/v5.8.3/#install-specific-release
ARG K3D_VERSION=v5.8.3
RUN curl --retry 5 --retry-all-errors -fsS https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh \
-o /tmp/k3d-install.sh \
&& TAG=${K3D_VERSION} bash /tmp/k3d-install.sh \
&& rm /tmp/k3d-install.sh
# The version of Kubernetes used by k3d is determined by the version of k3s it
# installs, which is determined by the version of k3d. Confirm that the expected
# Kubernetes version is indeed that k3d's default.
ARG KUBERNETES_VERSION=v1.31.5
RUN k3d version | grep -q "k3s version ${KUBERNETES_VERSION}-k3s"
# Install kubectl which we'll use to run integration tests. Must be compatible
# (+/- 1 version) with the Kubernetes used by the k3d installation above.
# See https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/#install-kubectl-binary-with-curl-on-linux
ARG KUBECTL_VERSION=$KUBERNETES_VERSION
RUN curl --retry 5 --retry-all-errors -fLO https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/${TARGETARCH}/kubectl \
&& chmod +x kubectl \
&& mv kubectl /usr/local/bin/kubectl
# Install Istio.
# See https://istio.io/latest/docs/setup/getting-started/
RUN curl --retry 5 --retry-all-errors -fL https://istio.io/downloadIstio \
-o /tmp/downloadIstio \
&& ISTIO_VERSION=${ISTIO_VERSION} sh /tmp/downloadIstio \
&& rm /tmp/downloadIstio \
&& mv istio-${ISTIO_VERSION}/bin/istioctl /usr/local/bin/istioctl \
&& chmod +x /usr/local/bin/istioctl \
&& rm -rf istio-${ISTIO_VERSION}
# Install Skaffold as per instructions here:
# https://skaffold.dev/docs/install/
# Latest version as of 2026-09-09.
ARG SKAFFOLD_VERSION=2.24.0
RUN curl --retry 5 --retry-all-errors -fLo skaffold https://storage.googleapis.com/skaffold/releases/v${SKAFFOLD_VERSION}/skaffold-linux-${TARGETARCH} \
&& chmod +x skaffold && mv skaffold /usr/local/bin/
# Skaffold will call kustomize via the `kustomize build` command; however, we
# get our `kustomize` from `kubectl kustomize`. So we need to alias `kustomize
# build` to `kubectl kustomize` in a way that works for all users of the system.
# We do this via a small wrapper script.
COPY .devcontainer/kustomize_wrapper.sh /usr/local/bin/kustomize
# Install crane based on instructions here:
# https://github.com/google/go-containerregistry/tree/main/cmd/crane#install-from-releases
# We use crane to move container images built by Bazel into Kubernetes clusters.
# Latest version as of 2026-09-09.
ARG CRANE_VERSION=0.22.1
RUN set -e; \
if [ "${TARGETARCH}" = "amd64" ]; then \
ARCH_SUFFIX="x86_64"; \
elif [ "${TARGETARCH}" = "arm64" ]; then \
ARCH_SUFFIX="arm64"; \
else \
echo "Unsupported arch: ${TARGETARCH}" && exit 1; \
fi; \
curl --retry 5 --retry-all-errors -fsL "https://github.com/google/go-containerregistry/releases/download/v${CRANE_VERSION}/go-containerregistry_linux_${ARCH_SUFFIX}.tar.gz" -o go-containerregistry.tar.gz \
&& tar -zxvf go-containerregistry.tar.gz -C /usr/local/bin/ crane \
&& rm go-containerregistry.tar.gz
# Install the Groundcover CLI based on instructions here:
# https://github.com/groundcover-com/cli#from-the-binary-releases
ARG GROUNDCOVER_VERSION=0.22.14
RUN curl --retry 5 --retry-all-errors -fSsL https://github.com/groundcover-com/cli/releases/download/v${GROUNDCOVER_VERSION}/groundcover_${GROUNDCOVER_VERSION}_linux_${TARGETARCH}.tar.gz -o /tmp/groundcover.tar.gz \
&& tar -zxf /tmp/groundcover.tar.gz -C /usr/bin \
&& chmod +x /usr/bin/groundcover
# Install the Ory CLI so developers can `ory auth` and manage Ory
# Network projects. The non-sqlite build is deliberate: the `sqlite`
# variants link against GLIBC 2.38, newer than this image (Ubuntu
# Jammy, GLIBC 2.35).
ARG ORY_VERSION=1.3.3
RUN set -e; \
if [ "${TARGETARCH}" = "amd64" ]; then \
ARCH_SUFFIX="64bit"; \
elif [ "${TARGETARCH}" = "arm64" ]; then \
ARCH_SUFFIX="arm64"; \
else \
echo "Unsupported arch: ${TARGETARCH}" && exit 1; \
fi; \
curl --retry 5 --retry-all-errors -fsL "https://github.com/ory/cli/releases/download/v${ORY_VERSION}/ory_${ORY_VERSION}-linux_${ARCH_SUFFIX}.tar.gz" -o ory.tar.gz \
&& tar -zxf ory.tar.gz -C /usr/local/bin/ ory \
&& rm ory.tar.gz \
&& chmod +x /usr/local/bin/ory
# Install the Envoy binary, so we can run the tests that use
# local-binary Envoy rather than Docker-based Envoy.
RUN set -e; \
if [ "${TARGETARCH}" = "amd64" ]; then \
ARCH_SUFFIX="x86_64"; \
elif [ "${TARGETARCH}" = "arm64" ]; then \
ARCH_SUFFIX="aarch_64"; \
else \
echo "Unsupported arch: ${TARGETARCH}" && exit 1; \
fi; \
BINARY_NAME="envoy-${ENVOY_VERSION}-linux-${ARCH_SUFFIX}"; \
wget --tries=5 --waitretry=10 --retry-connrefused https://github.com/envoyproxy/envoy/releases/download/v${ENVOY_VERSION}/${BINARY_NAME}; \
chmod +x ${BINARY_NAME}; \
mv ${BINARY_NAME} /usr/local/bin/envoy
# Install `ngrok`, useful in testing MCP servers from non-local clients
# like `claude.ai` - or to intercept traffic for inspection.
RUN curl --retry 5 --retry-all-errors -fsSL https://ngrok-agent.s3.amazonaws.com/ngrok.asc \
| sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null \
&& echo "deb https://ngrok-agent.s3.amazonaws.com bookworm main" \
| sudo tee /etc/apt/sources.list.d/ngrok.list \
&& sudo apt update \
&& sudo apt install ngrok
# Ensure presence of relevant system groups.
RUN groupadd -f --system docker
RUN groupadd -f --system ssh
RUN groupadd -f --system wheel
###############################################################################
# Create the image for our github codespaces. Most of the content here is taken
# from various microsoft and github resources linked below.
FROM respect-build-common AS codespace-workstation
ARG TARGETARCH
# Register relevant parameters for user creation.
ARG UNAME=vscode
ARG UID=1000
ARG GID=1000
# Codespace'ify this image
# Download script and run it with vscode options.
# For reference of options, see:
# https://github.com/microsoft/vscode-dev-containers/blob/v0.209.6/containers/ubuntu/.devcontainer/base.Dockerfile
# For reference on script run, see:
# https://github.com/microsoft/vscode-dev-containers/tree/main/script-library
# For further info, see:
# https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/docker.md
#
# Default to bash shell and set env var for the docker-init we are about to
# start.
ENV SHELL=/bin/bash \
DOCKER_BUILDKIT=1
ARG VSCODE_SCRIPTS_COMMIT=ef146121026c67d41bbca80d9af482f20f89f9e0
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install --no-install-recommends curl ca-certificates \
&& curl --retry 5 --retry-all-errors -fsSL "https://raw.githubusercontent.com/microsoft/vscode-dev-containers/${VSCODE_SCRIPTS_COMMIT}/script-library/common-debian.sh" -o /tmp/common-debian.sh \
&& bash /tmp/common-debian.sh "true" "${UNAME}" "${UID}" "${GID}" "true" \
&& rm /tmp/common-debian.sh \
&& apt-get clean -y && rm -rf /var/lib/apt/lists/*
# Finish setting up user account:
# * enable password-less sudo for users in group `wheel`; and
# * add users to relevant groups for ssh, docker and sudo.
RUN echo '%wheel ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
RUN usermod -a -G ssh,docker,wheel ${UNAME}
# Set entrypoint and command as part of the codespaceifying. See links above.
ENTRYPOINT ["/usr/local/share/ssh-init.sh", "/usr/local/share/docker-init.sh"]
CMD ["sleep", "infinity"]
# Install GitHub CLI through Codespace's preferred mechanism.
# https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/github-cli.md
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& curl --retry 5 --retry-all-errors -fsSL "https://raw.githubusercontent.com/microsoft/vscode-dev-containers/main/script-library/github-debian.sh" -o /tmp/github-debian.sh \
&& bash /tmp/github-debian.sh \
&& rm /tmp/github-debian.sh \
&& rm -rf /var/lib/apt/lists/*
# Ensure we have the most up to date version of the public key for
# 'https://cli.github.com/packages' since it has changed.
#
# See 'https://github.com/cli/cli/issues/6175' as well as the
# instructions for getting the most up to date public key from:
# https://github.com/cli/cli/commit/23933fd527b9f0e96faab24ac8ede8f8d9233448
#
# TODO(benh): do we need this once/if the 'FROM' gets updated correctly?
#
# TODO(gorm,rjh): try out whether this is still needed.
RUN curl --retry 5 --retry-all-errors -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
&& chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null
# Install buildifier from this releases page:
# https://github.com/bazelbuild/buildtools/releases
#
# We use the latest Buildifier version that matches our Bazel major version.
ARG BUILDIFIER_VERSION=6.4.0
RUN wget --tries=5 --waitretry=10 --retry-connrefused https://github.com/bazelbuild/buildtools/releases/download/v${BUILDIFIER_VERSION}/buildifier-linux-${TARGETARCH} \
&& chmod +x ./buildifier-linux-${TARGETARCH} \
&& mv ./buildifier-linux-${TARGETARCH} /usr/local/bin/buildifier
# Install `npm` as per these installation instructions:
# https://github.com/nodesource/distributions#installation-instructions
# Note: We need this to install prettier.
ARG NODE_MAJOR=26
ARG NPM_VERSION=11.19.1
RUN mkdir -p /etc/apt/keyrings \
&& curl --retry 5 --retry-all-errors -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
&& echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \
&& apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y nodejs \
&& npm install -g npm@${NPM_VERSION} \
&& rm -rf /var/lib/apt/lists/*
# Install prettier, markdown-autodocs, and ibazel (a tool for automatically
# rebuilding Bazel targets when their sources/dependencies change).
ARG PRETTIER_VERSION=2.7.1
ARG MARKDOWN_AUTODOCS_VERSION=1.0.133
ARG IBAZEL_VERSION=0.28.0
RUN npm install -g prettier@${PRETTIER_VERSION} markdown-autodocs@${MARKDOWN_AUTODOCS_VERSION} @bazel/ibazel@${IBAZEL_VERSION}
# Install bash-completion so tab autocomplete works for git and other commands.
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install bash-completion -y \
&& rm -rf /var/lib/apt/lists/*
# Install...
# * `yapf`, `mypy`, `isort` and `ruff` for linting and formatting of Python
# code. Keep the version of `mypy` in sync with `mypy-requirements.in`.
# * `build`, Python's package-builder-frontend.
# * `uv`, a fast Python package installer and resolver.
# * `py-spy`, the sampling profiler invoked by `_maybe_run_py_spy()`
# in `reboot/aio/monitoring.py`.
RUN pip install yapf==0.40.2 mypy==1.18.1 isort==5.12.0 ruff==0.1.14 build==1.6.0 uv==0.12.11 py-spy==0.4.2
# Install and setup fish (shell used on codespaces).
# NOTE: We do this as it is done here:
# https://github.com/devcontainers/images/blob/main/src/universal/.devcontainer/Dockerfile
# ...but, it is unclear if it is needed.
# TODO(gorm,rjh): try out whether this is still needed.
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -yq fish \
&& FISH_PROMPT="function fish_prompt\n set_color green\n echo -n (whoami)\n set_color normal\n echo -n \":\"\n set_color blue\n echo -n (pwd)\n set_color normal\n echo -n \"> \"\nend\n" \
&& printf "$FISH_PROMPT" >> /etc/fish/functions/fish_prompt.fish \
&& printf "if type code-insiders > /dev/null 2>&1; and not type code > /dev/null 2>&1\n alias code=code-insiders\nend" >> /etc/fish/conf.d/code_alias.fish \
&& rm -rf /var/lib/apt/lists/*
# Install Chrome - needed for @io_bazel_rules_webtesting.
# We need 'google-chrome-stable' for test purposes, we do not run tests on arm64
# (currently).
RUN if [ "${TARGETARCH}" = "amd64" ]; then \
wget --tries=5 --waitretry=10 --retry-connrefused -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
&& sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \
&& apt-get update -qqy \
&& DEBIAN_FRONTEND=noninteractive apt-get install -qqy google-chrome-stable; \
fi
# Install the shared libraries that Playwright's Chromium needs, on every
# architecture: the browser scenarios of `reboot.bdd` (e.g. the bank
# example's) download it with `playwright install chromium`, which does not
# install these, and Chrome above brings them along on amd64 only. Playwright
# itself knows the list for this distribution, so have it install them.
RUN npx --yes playwright install-deps chromium \
&& rm -rf /var/lib/apt/lists/* /root/.npm
# Install the AWS command line tool. AWS apparently does not provide different
# packages for their specific CLI versions, but they guarantee backwards
# compatibility throughout the major version:
# https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html
RUN set -e; \
if [ "${TARGETARCH}" = "amd64" ]; then \
ARCH_SUFFIX="x86_64"; \
elif [ "${TARGETARCH}" = "arm64" ]; then \
ARCH_SUFFIX="aarch64"; \
else \
echo "Unsupported arch: ${TARGETARCH}" && exit 1; \
fi; \
curl --retry 5 --retry-all-errors -f "https://awscli.amazonaws.com/awscli-exe-linux-${ARCH_SUFFIX}.zip" -o "awscliv2.zip" \
&& unzip awscliv2.zip \
&& ./aws/install \
&& rm awscliv2.zip
# Install `aws-iam-authenticator`, which `kubectl` uses to authenticate with
# AWS.
ARG AWS_IAM_AUTHENTICATOR_VERSION=0.7.20
RUN curl --retry 5 --retry-all-errors -fLo aws-iam-authenticator https://github.com/kubernetes-sigs/aws-iam-authenticator/releases/download/v${AWS_IAM_AUTHENTICATOR_VERSION}/aws-iam-authenticator_${AWS_IAM_AUTHENTICATOR_VERSION}_linux_${TARGETARCH} \
&& chmod +x ./aws-iam-authenticator \
&& mv ./aws-iam-authenticator /usr/local/bin/
# Install Pulumi, an Infrastructure-as-Code tool.
#
# Even if we don't use Pulumi CLI directly as humans, the Pulumi SDK (used by
# our code) requires that it is present.
ARG PULUMI_VERSION=3.261.0
RUN set -e; \
if [ "${TARGETARCH}" = "amd64" ]; then \
ARCH_SUFFIX="x64"; \
elif [ "${TARGETARCH}" = "arm64" ]; then \
ARCH_SUFFIX="arm64"; \
else \
echo "Unsupported arch: ${TARGETARCH}" && exit 1; \
fi; \
ARCHIVE_NAME="pulumi-v${PULUMI_VERSION}-linux-${ARCH_SUFFIX}.tar.gz"; \
wget --tries=5 --waitretry=10 --retry-connrefused https://get.pulumi.com/releases/sdk/${ARCHIVE_NAME} \
&& tar -xvf ${ARCHIVE_NAME} \
&& mv ./pulumi/* /usr/local/bin/ \
&& rm ${ARCHIVE_NAME}
# Install uv as the target user, and pre-fetch a few commonly used Python
# versions. (The latter is optional but enables the Python versions to be
# cached in the Docker image).
USER $UNAME
RUN curl --retry 5 --retry-all-errors -LsSf https://astral.sh/uv/0.12.11/install.sh \
-o /tmp/uv-install.sh \
&& sh /tmp/uv-install.sh \
&& rm /tmp/uv-install.sh \
&& "$HOME/.local/bin/uv" python install 3.10.13 3.11.8 3.12.2
# Bazel's `--incompatible_strict_action_env` causes a hardcoded PATH to be used which
# does not include the path where `uv` is installed. Rather than changing Bazel's PATH,
# we ensure that `uv` is accessible on it.
# TODO: See https://github.com/reboot-dev/mono/issues/2652.
# Use `-f`: base images / Dev Container features may already provide a
# `uv` there, and ours (pinned) must win.
RUN sudo ln -sf "$HOME/.local/bin/uv" /usr/local/bin/uv
RUN echo "export PATH=\"$HOME/.local/bin:\$PATH\"" >> "$HOME/.bashrc"
# Install Claude Code as the target user so it lands in the user's home
# (where its auto-updater writes), and symlink it onto the system PATH
# like `uv` above so non-interactive shells and other users find it too.
# It's a human-driven tool, so we don't pin the version and leave
# auto-update on.
RUN curl --retry 5 --retry-all-errors -fsSL https://claude.ai/install.sh \
-o /tmp/claude-install.sh \
&& bash /tmp/claude-install.sh \
&& rm /tmp/claude-install.sh \
&& sudo ln -sf "$HOME/.local/bin/claude" /usr/local/bin/claude
# Give Claude Code the headless Chrome DevTools MCP server in the user's
# config, so it can drive a browser out of the box.
RUN claude mcp add -s user chrome-devtools \
-- npx --yes chrome-devtools-mcp@latest --headless=true
# Then return to root.
USER root
# Install Helm.
ARG HELM_VERSION=3.21.4
RUN wget --tries=5 --waitretry=10 --retry-connrefused https://get.helm.sh/helm-v${HELM_VERSION}-linux-${TARGETARCH}.tar.gz \
&& tar --to-stdout -xvf ./helm-v${HELM_VERSION}-linux-${TARGETARCH}.tar.gz linux-${TARGETARCH}/helm > /usr/local/bin/helm \
&& rm ./helm-v${HELM_VERSION}-linux-${TARGETARCH}.tar.gz \
&& chmod +x /usr/local/bin/helm
# Install the Helm chart-testing tool.
ARG CHART_TESTING_VERSION=3.14.0
RUN wget --tries=5 --waitretry=10 --retry-connrefused https://github.com/helm/chart-testing/releases/download/v${CHART_TESTING_VERSION}/chart-testing_${CHART_TESTING_VERSION}_linux_${TARGETARCH}.tar.gz \
&& tar --to-stdout -xvf ./chart-testing_${CHART_TESTING_VERSION}_linux_${TARGETARCH}.tar.gz ct > /usr/local/bin/ct \
&& rm ./chart-testing_${CHART_TESTING_VERSION}_linux_${TARGETARCH}.tar.gz \
&& chmod +x /usr/local/bin/ct
# Install `pnpm` at a version compatible with our `aspect_rules_js`.
ARG PNPM_VERSION=8.15.8
RUN curl --retry 5 --retry-all-errors -fsSL https://get.pnpm.io/install.sh \
-o /tmp/pnpm-install.sh \
&& env PNPM_VERSION=${PNPM_VERSION} sh /tmp/pnpm-install.sh \
&& rm /tmp/pnpm-install.sh
# Install additional npm packages: `corepack` in order to get `yarn`, and the
# Firebase CLI.
ARG FIREBASE_VERSION=15.29.0
RUN npm install -g corepack firebase-tools@${FIREBASE_VERSION} \
&& corepack enable
# Install `kubectl krew` plugin manager, and the `resource-capacity`
# plugin. We use a shared `KREW_ROOT` so that any user can run `kubectl
# krew` without permission issues.
ARG KREW_VERSION=v0.5.0
ENV KREW_ROOT=/opt/krew
RUN set -e; \
case "${TARGETARCH}" in \
amd64|arm64) ;; \
*) echo "Unsupported arch for krew: ${TARGETARCH}" >&2; exit 1 ;; \
esac; \
TMPDIR="$(mktemp -d)" \
&& cd "${TMPDIR}" \
&& KREW="krew-linux_${TARGETARCH}" \
&& curl --retry 5 --retry-all-errors -fsSLO "https://github.com/kubernetes-sigs/krew/releases/download/${KREW_VERSION}/${KREW}.tar.gz" \
&& tar zxf "${KREW}.tar.gz" \
&& ./"${KREW}" install krew \
&& ln -s "${KREW_ROOT}/bin/kubectl-krew" /usr/local/bin/kubectl-krew \
&& kubectl krew install resource-capacity \
&& ln -s "${KREW_ROOT}/bin/kubectl-resource_capacity" /usr/local/bin/kubectl-resource_capacity \
&& chown -R ${UNAME}: "${KREW_ROOT}" \
&& chmod -R a+rX "${KREW_ROOT}" \
&& rm -rf "${TMPDIR}"
# Install the Codex CLI globally via npm so it's on PATH for every user.
# Like Claude Code (installed in the user's home above) it's a
# human-driven tool, so we don't pin the version.
RUN npm install -g @openai/codex
# Give Codex the same headless Chrome DevTools MCP server in the target
# user's config, mirroring the Claude Code setup above.
USER $UNAME
RUN codex mcp add chrome-devtools \
-- npx --yes chrome-devtools-mcp@latest --headless=true
USER root
# Android SDK + emulator + Maestro, for the React Native (Expo) example
# end-to-end tests. Those tests boot a headless, KVM-accelerated Android
# emulator and drive the app with Maestro. The emulator needs
# `/dev/kvm`; we add `vscode` to the `kvm` group (gid 109, the device's
# group at runtime) so it can use it without sudo. The AVD itself is
# created by the test script (cheap, and keeps the AVD/home paths out of
# the image). The emulator and its system image are x86_64-only and the
# test is tagged `requires-linux-x86`, so we install this only on amd64.
ENV ANDROID_HOME=/opt/android-sdk
ENV PATH="${ANDROID_HOME}/cmdline-tools/latest/bin:${ANDROID_HOME}/platform-tools:${ANDROID_HOME}/emulator:/home/${UNAME}/.maestro/bin:${PATH}"
ARG ANDROID_CMDLINE_TOOLS_VERSION=11076708
ARG ANDROID_CMDLINE_TOOLS_SHA256=2d2d50857e4eb553af5a6dc3ad507a17adf43d115264b1afc116f95c92e5e258
ARG ANDROID_API=34
# `dl.google.com` occasionally serves a truncated archive, so verify
# the command-line tools zip against its pinned checksum and give
# every download a few attempts before failing the build.
RUN if [ "${TARGETARCH}" = "amd64" ]; then \
set -e; \
apt-get update && apt-get install -y --no-install-recommends \
openjdk-17-jdk-headless unzip libpulse0 libnss3 libxcursor1 \
libxcomposite1 libasound2 libgl1 \
&& rm -rf /var/lib/apt/lists/*; \
mkdir -p "${ANDROID_HOME}/cmdline-tools"; \
downloaded=""; \
for attempt in 1 2 3; do \
if curl -fsSL "https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_CMDLINE_TOOLS_VERSION}_latest.zip" -o /tmp/cmdline-tools.zip \
&& echo "${ANDROID_CMDLINE_TOOLS_SHA256} /tmp/cmdline-tools.zip" | sha256sum -c -; \
then downloaded=1; break; fi; \
rm -f /tmp/cmdline-tools.zip; \
sleep 10; \
done; \
[ -n "${downloaded}" ]; \
unzip -q /tmp/cmdline-tools.zip -d "${ANDROID_HOME}/cmdline-tools"; \
mv "${ANDROID_HOME}/cmdline-tools/cmdline-tools" "${ANDROID_HOME}/cmdline-tools/latest"; \
rm /tmp/cmdline-tools.zip; \
yes | "${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" --licenses >/dev/null; \
installed=""; \
for attempt in 1 2 3; do \
if "${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" \
"platform-tools" "emulator" \
"platforms;android-${ANDROID_API}" \
"system-images;android-${ANDROID_API};google_apis;x86_64" >/dev/null \
&& test -d "${ANDROID_HOME}/platform-tools" \
&& test -d "${ANDROID_HOME}/emulator" \
&& test -d "${ANDROID_HOME}/platforms/android-${ANDROID_API}" \
&& test -d "${ANDROID_HOME}/system-images/android-${ANDROID_API}/google_apis/x86_64"; \
then installed=1; break; fi; \
rm -rf "${ANDROID_HOME}/.temp" "${ANDROID_HOME}/platform-tools" \
"${ANDROID_HOME}/emulator" "${ANDROID_HOME}/platforms" \
"${ANDROID_HOME}/system-images"; \
sleep 10; \
done; \
[ -n "${installed}" ]; \
chown -R ${UNAME}: "${ANDROID_HOME}"; \
groupadd -g 109 kvm 2>/dev/null || true; \
usermod -aG kvm ${UNAME}; \
fi
# Maestro CLI (installs into the target user's home; needs the JDK
# above). Only on amd64, matching the SDK install above.
USER $UNAME
RUN if [ "${TARGETARCH}" = "amd64" ]; then \
curl --retry 5 --retry-all-errors -fsSL "https://get.maestro.mobile.dev" -o /tmp/maestro-install.sh \
&& bash /tmp/maestro-install.sh \
&& rm /tmp/maestro-install.sh; \
fi
# Warm the npm cache with the packages `rbt generate` installs into a
# project's `.rbt` directory, and with their dependencies. This
# version must match the one `_check_or_install_npm_packages` asks for
# in `reboot/cli/commands/generate.py`. When a test runs `rbt dev run`
# against a fresh state directory it installs these packages, and a
# warm cache lets it do that without a registry round-trip. A real
# resolution is what walks the dependency graph, so this installs the
# three into a throwaway prefix, leaving the whole closure in the
# cache; it also fails the build if the pin stops resolving.
ARG BUFBUILD_VERSION=1.10.1
RUN npm install --prefix /tmp/npm-cache-warm \
@bufbuild/protoplugin@${BUFBUILD_VERSION} \
@bufbuild/protoc-gen-es@${BUFBUILD_VERSION} \
@bufbuild/protobuf@${BUFBUILD_VERSION} \
&& rm -rf /tmp/npm-cache-warm
USER root
###############################################################################
# The following is a partial copy-paste from
# `reboot/containers/reboot-base/Dockerfile`; keep these in sync!
#
# TODO(rjh): instead of copy-pasting, can we share code?
FROM envoyproxy/envoy:v${ENVOY_VERSION} AS reboot-envoy
### Reboot requirements.
# Reboot is built on Python, so we need to install it.
# Ubuntu Jammy's default Python version is 3.10, which is also Reboot's
# required Python version.
ARG PYTHON_VERSION=3.10
# Installs:
# * `curl` to support debugging.
# * `python3` itself.
# * `python3-pip` to allow installing of python packages.
# * `python3-dev` and `build-essential` to allow `node-gyp` to build native Node
# modules, notably `@reboot-dev/reboot`.
# * `python-is-python3` to make sure we're using python3 (not python2).
# * `tini` to provide an entrypoint with proper signal handling in the
# container, which is important for graceful shutdowns and
# proper handling of signals like `SIGTERM` and `SIGINT`.
# * `git` since the Reboot CLI relies on it to do its `.rbtrc` logic.
# * `netcat-openbsd` to provide `nc` which is used in some of our tests.
#
# TODO(rjh): we can probably reduce the final image size somewhat if we do
# multi-stage builds, in which e.g. `python3-dev` and
# `build-essential` are only installed during a build stage, and are
# absent in the final runtime container - at the expense of a more
# complex Dockerfile.
# This image starts from an upstream base rather than from
# `respect-current-minimum`, so it needs the same `apt` retries.
RUN echo 'Acquire::Retries "5";' > /etc/apt/apt.conf.d/80-retries
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
python3 \
python3-pip \
python3-dev \
build-essential \
python-is-python3 \
tini \
git \
netcat-openbsd \
# Confirm that python3 is installed and that it has the expected version.
&& python3 --version | grep -Eq "^Python ${PYTHON_VERSION}.*" \
# And that "python" (aka python3) is installed and has the expected version.
&& python --version | grep -Eq "^Python ${PYTHON_VERSION}.*" \
# Clean up, leaving a smaller layer.
&& rm -rf /var/lib/apt/lists/*
# Envoy's default entrypoint would run the customer-supplied `CMD` under
# the `envoy` user. Some serving platforms (e.g. Fly.io) mount volumes
# in the expectation that we are `root`. We must therefore override the
# entrypoint with a custom one that runs as `root`.
ENTRYPOINT ["/usr/bin/tini", "--"]
########################################################################
# A manylinux-based image for building C++ code with maximum
# compatibility.
#
# This image uses `manylinux_2_34` (glibc 2.34, AlmaLinux 9) as the
# base, which produces binaries compatible with a wide range of Linux
# distributions including Ubuntu 22.04+, Debian 12+, and similar.
#
# We install Clang to match our main build environment, plus Bazel and
# Python 3.10 for building the reboot wheel.
#
# NOTE: because the `manylinux` images have architecture-specific names
# that don't match Docker's `TARGETARCH` argument we use two
# `FROM` statements to rename these to `TARGETARCH`-compatible
# names.
FROM quay.io/pypa/manylinux_2_34_x86_64 AS manylinux-builder-amd64
FROM quay.io/pypa/manylinux_2_34_aarch64 AS manylinux-builder-arm64
# Select the appropriate base image based on `TARGETARCH`.
ARG TARGETARCH
FROM manylinux-builder-${TARGETARCH} AS manylinux-builder
ARG TARGETARCH
ARG CLANG_VERSION
# Install basic build dependencies.
RUN dnf install -y \
git \
wget \
which \
patch \
zip \
unzip \
&& dnf clean all
# Install Clang from the AlmaLinux AppStream repo.
#
# We create symlinks at /usr/lib/llvm-${CLANG_VERSION}/bin/ to match
# the host workstation's paths, so Bazel's auto-detected toolchain works
# in both environments.
RUN dnf install -y clang gcc-c++ && dnf clean all \
&& mkdir -p /usr/lib/llvm-${CLANG_VERSION}/bin \
&& ln -sf /usr/bin/clang /usr/lib/llvm-${CLANG_VERSION}/bin/clang \
&& ln -sf /usr/bin/clang++ /usr/lib/llvm-${CLANG_VERSION}/bin/clang++ \
&& mkdir -p /usr/lib/llvm-${CLANG_VERSION}/lib \
&& ln -sf /usr/lib/clang /usr/lib/llvm-${CLANG_VERSION}/lib/clang
# The `manylinux` image comes with multiple Python versions, but all
# of them are built `--disable-shared` with no `libpython` installed,
# and embedding Python — which building `reboot_native.node` does —
# needs `-lpython3.10` at link time. Point `python` and `python3` at a
# python-build-standalone CPython 3.10 instead, which ships
# `libpython3.10.so`; it is the same build
# `reboot/nodejs/prepare_environment.sh` uses. `pip` and `pip3` stay
# on the manylinux 3.10, whose layout `auditwheel` and the wheel
# builds expect, so `python3` and `pip` deliberately name different
# installations.
RUN set -e; \
if [ "${TARGETARCH}" = "amd64" ]; then ARCH=x86_64; else ARCH=aarch64; fi; \
mkdir /tmp/python-build-standalone; \
wget -qO- "https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.10.14+20240814-${ARCH}-unknown-linux-gnu-install_only.tar.gz" \
| tar -xzf - -C /tmp/python-build-standalone \
&& mv /tmp/python-build-standalone/python /opt/reboot-python \
&& rmdir /tmp/python-build-standalone \
&& ln -sf /opt/reboot-python/bin/python3 /usr/local/bin/python \
&& ln -sf /opt/reboot-python/bin/python3 /usr/local/bin/python3 \
&& ln -sf /opt/python/cp310-cp310/bin/pip /usr/local/bin/pip \
&& ln -sf /opt/python/cp310-cp310/bin/pip /usr/local/bin/pip3
# Install Bazel via Bazelisk.
ARG BAZELISK_VERSION=v1.29.0
RUN set -e; \
if [ "${TARGETARCH}" = "amd64" ]; then \
ARCH_SUFFIX="amd64"; \
elif [ "${TARGETARCH}" = "arm64" ]; then \
ARCH_SUFFIX="arm64"; \
else \
echo "Unsupported arch: ${TARGETARCH}" && exit 1; \
fi; \
wget --tries=5 --waitretry=10 --retry-connrefused -O /usr/local/bin/bazel https://github.com/bazelbuild/bazelisk/releases/download/${BAZELISK_VERSION}/bazelisk-linux-${ARCH_SUFFIX} \
&& chmod +x /usr/local/bin/bazel
# Install Python build dependencies.
RUN pip install build wheel auditwheel
# Set up environment for Bazel to use Clang.
ENV CC=/usr/bin/clang
ENV CXX=/usr/bin/clang++
# Force include <cstdint> to fix RocksDB build with strict clang 20.
ENV CFLAGS="-include cstdint"
ENV CXXFLAGS="-include cstdint"
# Create a non-root user for running Bazel. rules_python's hermetic
# Python interpreter refuses to run as root.
ARG BUILDER_UID=1000
ARG BUILDER_GID=1000
RUN groupadd -g ${BUILDER_GID} builder \
&& useradd -m -u ${BUILDER_UID} -g ${BUILDER_GID} builder
USER builder
WORKDIR /workspace
########################################################################
# A manylinux-based image for running tests with the built binaries.
#
# This extends `manylinux-builder` by adding the Envoy binary, enabling
# tests to run with `REBOOT_LOCAL_ENVOY_MODE=executable`.
FROM manylinux-builder AS manylinux-runner
ARG TARGETARCH
ARG ENVOY_VERSION
# Switch to root to install Envoy system-wide.
USER root
# Install the Envoy binary for running tests with executable Envoy mode.
RUN set -e; \
if [ "${TARGETARCH}" = "amd64" ]; then \
ARCH_SUFFIX="x86_64"; \
elif [ "${TARGETARCH}" = "arm64" ]; then \
ARCH_SUFFIX="aarch_64"; \
else \
echo "Unsupported arch: ${TARGETARCH}" && exit 1; \
fi; \
BINARY_NAME="envoy-${ENVOY_VERSION}-linux-${ARCH_SUFFIX}"; \
wget --tries=5 --waitretry=10 --retry-connrefused https://github.com/envoyproxy/envoy/releases/download/v${ENVOY_VERSION}/${BINARY_NAME}; \
chmod +x ${BINARY_NAME}; \
mv ${BINARY_NAME} /usr/local/bin/envoy
# Install Node.js (which includes npm) for running JavaScript-based tests.
RUN dnf install -y nodejs && dnf clean all
# Install uv for the builder user. We create a symlink in /usr/local/bin
# so it's accessible on the PATH regardless of the user.
USER builder
RUN curl --retry 5 --retry-all-errors -LsSf https://astral.sh/uv/0.12.11/install.sh \
-o /tmp/uv-install.sh \
&& sh /tmp/uv-install.sh \
&& rm /tmp/uv-install.sh
USER root
RUN ln -sf /home/builder/.local/bin/uv /usr/local/bin/uv
# Switch back to the builder user for running tests.
USER builder