Skip to content

Commit 581c12e

Browse files
authored
refa(devcontainer): rework to use mise for fast switching (#2948)
* refa(devcontainer): rework to use mise for fast switching * fix(ci): update workflows for mise-based devcontainer * fix(e2e): use playwright-managed chromium * refa(workflows): update build_images and e2e_tests for Ruby 4.0 and 3.4 * fix(devcontainer): set DEBIAN_FRONTEND to noninteractive for apt-get install * fix(devcontainer): correct mise command usage in entrypoint script * fix(devcontainer): remove unnecessary trust command from entrypoint script * fix(e2e): use dynamic IMAGE_TAG for Docker image in e2e tests * fix(devcontainer): set DEBIAN_FRONTEND environment variable before apt-get install * fix(devcontainer): comment out pull_request trigger in build_images.yml * fix(devcontainer): update base image and Ruby version in .env.example * fix(devcontainer): versioned containers * fix(devcontainer): enable manual workflow dispatch and clean up comments * fix(devcontainer): update image tagging to use version from .devcontainer/VERSION * fix(devcontainer): add comments for triggering workflow in topic branches * fix(devcontainer): update BASE_IMAGE to use Ubuntu 24.04 * fix(devcontainer): clean up .env.example by removing legacy comments and base image * fix(devcontainer): add e2e profile to sentry-test service and update workflow * fix(devcontainer): pre-install Java 21 in Dockerfile for JRuby support * chore(mise): set ruby version to 'ruby@latest' and add java version to tools * fix(devcontainer): pre-install precompiled Ruby version in Dockerfile * chore(devcontainer): remove dead method from setup * refa(devcontainer): install ruby only as part of the build process * fix(devcontainer): widen glob for finding chrome bin for capybara symlink
1 parent f2b412c commit 581c12e

13 files changed

Lines changed: 205 additions & 195 deletions

File tree

.devcontainer/.env.example

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
# Official Ruby images
2-
IMAGE="ruby:3.4.5-slim-bookworm"
3-
VERSION="3.4.5"
4-
5-
# IMAGE="jruby:latest"
1+
# Devcontainer configuration
2+
RUBY_VERSION="3.4.9"
63

74
# E2E testing
85
SENTRY_DSN="http://user:pass@sentry.localhost/project/42"

.devcontainer/Dockerfile

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
ARG IMAGE="ruby"
1+
ARG BASE_IMAGE=ubuntu:24.04
2+
FROM ${BASE_IMAGE}
23

3-
FROM ${IMAGE} AS build
4+
ENV DEBIAN_FRONTEND=noninteractive
45

56
RUN apt-get update && apt-get install -y --no-install-recommends \
67
sudo \
78
gnupg \
89
git \
910
curl \
1011
wget \
12+
ca-certificates \
1113
build-essential \
1214
pkg-config \
1315
libssl-dev \
@@ -21,27 +23,61 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
2123
libgdbm-dev \
2224
sqlite3 \
2325
libsqlite3-dev \
26+
tzdata \
2427
&& apt-get clean \
2528
&& rm -rf /var/lib/apt/lists/*
2629

2730
RUN echo "sentry ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/sentry \
2831
&& chmod 0440 /etc/sudoers.d/sentry
2932

30-
RUN groupadd --gid 1000 sentry \
33+
RUN userdel -r ubuntu 2>/dev/null || true \
34+
&& groupdel ubuntu 2>/dev/null || true \
35+
&& groupadd --gid 1000 sentry \
3136
&& useradd --uid 1000 --gid sentry --shell /bin/bash --create-home sentry
3237

3338
WORKDIR /workspace/sentry
3439

3540
RUN chown -R sentry:sentry /workspace/sentry
3641

37-
ARG VERSION
38-
ARG GEM_HOME="/workspace/sentry/vendor/gems/${VERSION}"
39-
4042
ENV LANG=C.UTF-8 \
4143
BUNDLE_JOBS=4 \
4244
BUNDLE_RETRY=3 \
43-
GEM_HOME=/workspace/sentry/vendor/gems/${VERSION} \
44-
PATH=$PATH:${GEM_HOME}/bin \
45-
REDIS_HOST=redis
45+
REDIS_HOST=redis \
46+
PATH=/home/sentry/.local/share/mise/shims:/home/sentry/.local/bin:$PATH
4647

4748
USER sentry
49+
50+
# Pre-create the bundle directory owned by sentry so that a Docker named volume
51+
# mounted here is initialised with the correct ownership (Docker copies image
52+
# directory content when a volume is first attached).
53+
RUN mkdir -p /home/sentry/bundle
54+
55+
RUN curl https://mise.run | sh \
56+
&& echo 'eval "$(/home/sentry/.local/bin/mise activate bash)"' >> /home/sentry/.bashrc \
57+
&& echo 'eval "$(/home/sentry/.local/bin/mise activate zsh)"' >> /home/sentry/.zshenv
58+
59+
# Pre-install Ruby at build time so the container starts immediately without
60+
# downloading it at runtime. Switching Ruby versions requires a rebuild.
61+
ARG RUBY_VERSION=latest
62+
63+
# Java is always installed (required for JRuby). It is listed in .mise.toml so
64+
# it is available for all users regardless of which Ruby flavour they use.
65+
RUN echo "📦 Pre-installing java@temurin-21..." && \
66+
/home/sentry/.local/bin/mise install "java@temurin-21" && \
67+
/home/sentry/.local/bin/mise use --global "java@temurin-21"
68+
69+
RUN echo "📦 Pre-installing ruby@${RUBY_VERSION} (precompiled)..." && \
70+
MISE_RUBY_COMPILE=0 /home/sentry/.local/bin/mise install "ruby@${RUBY_VERSION}" && \
71+
/home/sentry/.local/bin/mise use --global "ruby@${RUBY_VERSION}"
72+
73+
# Node.js is always needed for the svelte-mini e2e app.
74+
RUN echo "📦 Pre-installing node@lts..." && \
75+
/home/sentry/.local/bin/mise install "node@lts" && \
76+
/home/sentry/.local/bin/mise use --global "node@lts"
77+
78+
# Install headless Chromium via Playwright (includes all system dependencies).
79+
# Symlink the binary into ~/.local/bin which is already on PATH.
80+
RUN /home/sentry/.local/share/mise/shims/npx playwright install chromium --with-deps
81+
# Playwright lays out the binary under chrome-linux/ on arm64 and chrome-linux64/
82+
# on x86_64 (since Playwright 1.57), so the glob has to match both.
83+
RUN bash -c 'ln -sf /home/sentry/.cache/ms-playwright/chromium-*/chrome-linux*/chrome /home/sentry/.local/bin/chromium'

.devcontainer/VERSION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"features": {
3+
"ghcr.io/devcontainers-extra/features/npm-packages:latest": {
4+
"version": "1.0.1",
5+
"resolved": "ghcr.io/devcontainers-extra/features/npm-packages@sha256:0851cc312204f4044f22230986134026409565f9e632d8ab2b8c639e81cedd7c",
6+
"integrity": "sha256:0851cc312204f4044f22230986134026409565f9e632d8ab2b8c639e81cedd7c"
7+
},
8+
"ghcr.io/devcontainers/features/github-cli:latest": {
9+
"version": "1.1.0",
10+
"resolved": "ghcr.io/devcontainers/features/github-cli@sha256:d22f50b70ed75339b4eed1ba9ecde3a1791f90e88d37936517e3bace0bbad671",
11+
"integrity": "sha256:d22f50b70ed75339b4eed1ba9ecde3a1791f90e88d37936517e3bace0bbad671"
12+
},
13+
"ghcr.io/devcontainers/features/node:latest": {
14+
"version": "2.0.0",
15+
"resolved": "ghcr.io/devcontainers/features/node@sha256:fedd4c11f7adfb64283b578dddc7da906728daa25fa293351c9d913231acf12f",
16+
"integrity": "sha256:fedd4c11f7adfb64283b578dddc7da906728daa25fa293351c9d913231acf12f"
17+
},
18+
"ghcr.io/nils-geistmann/devcontainers-features/zsh:latest": {
19+
"version": "0.0.8",
20+
"resolved": "ghcr.io/nils-geistmann/devcontainers-features/zsh@sha256:fd57a61a5187480b5e73f8041be5b67005be48f06503736df6cfdd8d0f38f3c4",
21+
"integrity": "sha256:fd57a61a5187480b5e73f8041be5b67005be48f06503736df6cfdd8d0f38f3c4"
22+
},
23+
"ghcr.io/rocker-org/devcontainer-features/apt-packages:latest": {
24+
"version": "1.0.2",
25+
"resolved": "ghcr.io/rocker-org/devcontainer-features/apt-packages@sha256:87a4d7750a596a5db034ba8508782f31aebdc2ffe955c66aaecb33d9de2ecdae",
26+
"integrity": "sha256:87a4d7750a596a5db034ba8508782f31aebdc2ffe955c66aaecb33d9de2ecdae"
27+
}
28+
}
29+
}

.devcontainer/devcontainer.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,11 @@
55
"workspaceFolder": "/workspace/sentry",
66
"features": {
77
"ghcr.io/devcontainers/features/github-cli:latest": {},
8-
"ghcr.io/devcontainers-extra/features/mise:latest": {},
98
"ghcr.io/nils-geistmann/devcontainers-features/zsh:latest": {},
109
"ghcr.io/devcontainers/features/node:latest": {},
1110
"ghcr.io/devcontainers-extra/features/npm-packages:latest": {},
1211
"ghcr.io/rocker-org/devcontainer-features/apt-packages:latest": {
13-
"packages": "inotify-tools"
12+
"packages": "inotify-tools,tzdata"
1413
}
1514
},
1615
"customizations": {

.devcontainer/docker-compose.yml

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,29 @@ services:
55
context: ..
66
dockerfile: .devcontainer/Dockerfile
77
args:
8-
IMAGE: ${IMAGE}
9-
VERSION: ${VERSION}
8+
BASE_IMAGE: ${BASE_IMAGE:-ubuntu:24.04}
9+
RUBY_VERSION: ${RUBY_VERSION:-latest}
1010
volumes:
1111
- ..:/workspace/sentry:cached
1212
working_dir: /workspace/sentry
1313
env_file: [".env"]
1414

1515
sentry-dev:
1616
<<: *sentry-build
17-
entrypoint: ".devcontainer/run --service dev"
18-
command: "sleep infinity"
17+
entrypoint: [".devcontainer/run"]
1918
depends_on:
2019
- redis
2120

2221
sentry-test:
2322
<<: *sentry-build
24-
entrypoint: ".devcontainer/run --service test"
25-
command: ["foreman", "start"]
23+
profiles: ["e2e"]
24+
entrypoint: [".devcontainer/run"]
25+
command: ["mise", "run", "e2e:serve"]
26+
environment:
27+
BUNDLE_PATH: /home/sentry/bundle
28+
volumes:
29+
- ..:/workspace/sentry:cached
30+
- bundle-gems:/home/sentry/bundle
2631
ports:
2732
- "${SENTRY_E2E_RAILS_APP_PORT}:4000"
2833
- "${SENTRY_E2E_SVELTE_APP_PORT}:4001"
@@ -33,3 +38,6 @@ services:
3338
- ALLOW_EMPTY_PASSWORD=yes
3439
ports:
3540
- "6379:6379"
41+
42+
volumes:
43+
bundle-gems:

.devcontainer/run

Lines changed: 23 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,35 @@
11
#!/bin/bash
2-
3-
set -e
2+
#
3+
# Container entrypoint. Ruby and other toolchains are pre-installed in the
4+
# image; this script only handles per-run setup (ownership, mise activation)
5+
# and hands off to whatever docker-compose passes as `command`.
6+
#
7+
set -euo pipefail
48

59
cd /workspace/sentry
610

7-
sudo mkdir -p vendor/gems
8-
sudo chown -R sentry:sentry vendor/gems
9-
10-
# git config --global --replace-all safe.directory /workspace/sentry
11-
# git config --global --replace-all safe.directory /workspace/sentry/vendor/gems/*
12-
13-
sudo chown -R sentry:sentry .
14-
15-
run_service_setup() {
16-
local service="$1"
17-
18-
echo "🚀 Running setup for service: $service"
19-
20-
case "$service" in
21-
"dev")
22-
if ! .devcontainer/setup --with-foreman --only-bundle; then
23-
echo "❌ Setup failed for service: $service"
24-
exit 1
25-
fi
26-
;;
27-
"test")
28-
if ! .devcontainer/setup --with-foreman --only .,spec/apps/rails-mini; then
29-
echo "❌ Setup failed for service: $service"
30-
exit 1
31-
fi
32-
;;
33-
*)
34-
echo "❌ Unknown service: $service"
35-
echo "Available services: dev, test"
36-
exit 1
37-
;;
38-
esac
11+
# In CI the workspace is checked out by the runner user (e.g. UID 1001) and
12+
# then bind-mounted into the container where we run as sentry (UID 1000).
13+
# Fix ownership once here so bundler, git, etc. can write freely.
14+
sudo chown -R sentry:sentry /workspace/sentry 2>/dev/null || true
15+
MISE_BIN="/home/sentry/.local/bin/mise"
3916

40-
echo "✅ Setup completed for service: $service"
41-
}
17+
# Git also refuses to operate in directories owned by a different user.
18+
git config --global --add safe.directory /workspace/sentry 2>/dev/null || true
4219

43-
# Function to start services in background
44-
start_services_if_needed() {
45-
# Check if we're running tests (bundle exec rake)
46-
if [[ "$*" == *"bundle exec rake"* ]]; then
47-
echo "🚀 Starting e2e services in background for test execution..."
48-
49-
# Start foreman in background
50-
foreman start &
51-
FOREMAN_PID=$!
52-
53-
# Wait for services to be ready
54-
echo "⏳ Waiting for services to start..."
55-
for i in {1..30}; do
56-
if curl -f http://localhost:4000/health >/dev/null 2>&1 && \
57-
curl -f http://localhost:4001/health >/dev/null 2>&1; then
58-
echo "✅ Services are ready!"
59-
break
60-
fi
61-
62-
if [ $i -eq 30 ]; then
63-
echo "❌ Services failed to start within timeout"
64-
kill $FOREMAN_PID 2>/dev/null || true
65-
exit 1
66-
fi
67-
68-
sleep 2
69-
done
70-
71-
# Set up cleanup trap
72-
trap "echo '🧹 Stopping services...'; kill $FOREMAN_PID 2>/dev/null || true; wait $FOREMAN_PID 2>/dev/null || true" EXIT
73-
fi
74-
}
20+
if [[ ! -x "$MISE_BIN" ]]; then
21+
echo "❌ mise not found at $MISE_BIN (it should be installed by the Dockerfile)"
22+
exit 1
23+
fi
7524

76-
# Parse arguments
77-
if [ "$1" = "--service" ] && [ -n "$2" ]; then
78-
service="$2"
79-
shift 2
25+
# Activate mise for this shell so PATH/shims are resolved correctly.
26+
eval "$("$MISE_BIN" activate bash)"
8027

81-
run_service_setup "$service"
28+
# Trust the workspace config so mise will use it without prompting.
29+
"$MISE_BIN" trust /workspace/sentry >/dev/null
8230

83-
if [ $# -gt 0 ]; then
84-
start_services_if_needed "$@"
85-
exec "$@"
86-
else
87-
exec bash
88-
fi
31+
if [[ $# -eq 0 ]]; then
32+
exec sleep infinity
8933
else
90-
start_services_if_needed "$@"
9134
exec "$@"
9235
fi

.devcontainer/setup

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ class SetupScript
3535

3636
if should_run_bundle?
3737
cleanup_ruby_lsp_directories
38-
update_rubygems_and_bundler
3938
install_bundle_dependencies
4039
install_foreman_gem if @options[:with_foreman]
4140
end
@@ -123,7 +122,7 @@ class SetupScript
123122
Dir.chdir(folder_path) do
124123
puts " Installing dependencies for #{folder_path}..."
125124

126-
unless system("[ -f Gemfile.lock ] && rm Gemfile.lock; bundle install")
125+
unless system("bundle install")
127126
puts "❌ Bundle install failed for #{folder}"
128127
exit 1
129128
end
@@ -136,28 +135,6 @@ class SetupScript
136135
end
137136
end
138137

139-
def update_rubygems_and_bundler
140-
puts "📦 Updating RubyGems and Bundler..."
141-
142-
if RUBY_VERSION >= "3.0"
143-
unless system("sudo gem update --system --silent")
144-
puts "❌ RubyGems update failed"
145-
exit 1
146-
end
147-
else
148-
unless system("sudo gem update --silent --system 3.4.22")
149-
puts "❌ RubyGems update failed"
150-
exit 1
151-
end
152-
153-
# sentry-sidekiq does not bundle with Bundler 2.5.x that ships with RubyGems 3.4.22
154-
unless system("sudo gem install bundler -v 2.4.22")
155-
puts "❌ Bundler installation failed"
156-
exit 1
157-
end
158-
end
159-
end
160-
161138
def install_foreman_gem
162139
unless system('gem install foreman')
163140
puts "❌ Foreman gem installation failed"

0 commit comments

Comments
 (0)