diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index d083a7abc..e833250ed 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,6 +1,4 @@ -ARG IMAGE="ruby" - -FROM ${IMAGE} AS build +FROM ubuntu:latest RUN apt-get update && apt-get install -y --no-install-recommends \ sudo \ @@ -8,6 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ git \ curl \ wget \ + ca-certificates \ build-essential \ pkg-config \ libssl-dev \ @@ -27,21 +26,32 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ RUN echo "sentry ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/sentry \ && chmod 0440 /etc/sudoers.d/sentry -RUN groupadd --gid 1000 sentry \ +RUN userdel -r ubuntu 2>/dev/null || true \ + && groupdel ubuntu 2>/dev/null || true \ + && groupadd --gid 1000 sentry \ && useradd --uid 1000 --gid sentry --shell /bin/bash --create-home sentry WORKDIR /workspace/sentry RUN chown -R sentry:sentry /workspace/sentry -ARG VERSION -ARG GEM_HOME="/workspace/sentry/vendor/gems/${VERSION}" - ENV LANG=C.UTF-8 \ BUNDLE_JOBS=4 \ BUNDLE_RETRY=3 \ - GEM_HOME=/workspace/sentry/vendor/gems/${VERSION} \ - PATH=$PATH:${GEM_HOME}/bin \ - REDIS_HOST=redis + REDIS_HOST=redis \ + PATH=/home/sentry/.local/bin:$PATH USER sentry + +RUN curl https://mise.run | sh \ + && echo 'eval "$(/home/sentry/.local/bin/mise activate zsh)"' >> /home/sentry/.zshenv + +# When RUBY_VERSION is provided at build time, pre-install that Ruby so the +# container starts immediately without downloading it at runtime. When it is +# empty (local dev builds) Ruby is installed lazily by the `run` entrypoint. +ARG RUBY_VERSION="" +RUN if [ -n "${RUBY_VERSION}" ]; then \ + echo "๐Ÿ“ฆ Pre-installing ruby@${RUBY_VERSION} (precompiled)..." && \ + MISE_RUBY_COMPILE=0 /home/sentry/.local/bin/mise install "ruby@${RUBY_VERSION}" && \ + /home/sentry/.local/bin/mise use --global "ruby@${RUBY_VERSION}"; \ + fi diff --git a/.devcontainer/devcontainer-lock.json b/.devcontainer/devcontainer-lock.json new file mode 100644 index 000000000..1cf373bb3 --- /dev/null +++ b/.devcontainer/devcontainer-lock.json @@ -0,0 +1,29 @@ +{ + "features": { + "ghcr.io/devcontainers-extra/features/npm-packages:latest": { + "version": "1.0.1", + "resolved": "ghcr.io/devcontainers-extra/features/npm-packages@sha256:0851cc312204f4044f22230986134026409565f9e632d8ab2b8c639e81cedd7c", + "integrity": "sha256:0851cc312204f4044f22230986134026409565f9e632d8ab2b8c639e81cedd7c" + }, + "ghcr.io/devcontainers/features/github-cli:latest": { + "version": "1.1.0", + "resolved": "ghcr.io/devcontainers/features/github-cli@sha256:d22f50b70ed75339b4eed1ba9ecde3a1791f90e88d37936517e3bace0bbad671", + "integrity": "sha256:d22f50b70ed75339b4eed1ba9ecde3a1791f90e88d37936517e3bace0bbad671" + }, + "ghcr.io/devcontainers/features/node:latest": { + "version": "2.0.0", + "resolved": "ghcr.io/devcontainers/features/node@sha256:fedd4c11f7adfb64283b578dddc7da906728daa25fa293351c9d913231acf12f", + "integrity": "sha256:fedd4c11f7adfb64283b578dddc7da906728daa25fa293351c9d913231acf12f" + }, + "ghcr.io/nils-geistmann/devcontainers-features/zsh:latest": { + "version": "0.0.8", + "resolved": "ghcr.io/nils-geistmann/devcontainers-features/zsh@sha256:fd57a61a5187480b5e73f8041be5b67005be48f06503736df6cfdd8d0f38f3c4", + "integrity": "sha256:fd57a61a5187480b5e73f8041be5b67005be48f06503736df6cfdd8d0f38f3c4" + }, + "ghcr.io/rocker-org/devcontainer-features/apt-packages:latest": { + "version": "1.0.2", + "resolved": "ghcr.io/rocker-org/devcontainer-features/apt-packages@sha256:87a4d7750a596a5db034ba8508782f31aebdc2ffe955c66aaecb33d9de2ecdae", + "integrity": "sha256:87a4d7750a596a5db034ba8508782f31aebdc2ffe955c66aaecb33d9de2ecdae" + } + } +} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index f21983fb3..4f89e2184 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,12 +5,11 @@ "workspaceFolder": "/workspace/sentry", "features": { "ghcr.io/devcontainers/features/github-cli:latest": {}, - "ghcr.io/devcontainers-extra/features/mise:latest": {}, "ghcr.io/nils-geistmann/devcontainers-features/zsh:latest": {}, "ghcr.io/devcontainers/features/node:latest": {}, "ghcr.io/devcontainers-extra/features/npm-packages:latest": {}, "ghcr.io/rocker-org/devcontainer-features/apt-packages:latest": { - "packages": "inotify-tools" + "packages": "inotify-tools,tzdata" } }, "customizations": { diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index a85243cba..50e01bbaf 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -4,9 +4,6 @@ services: build: context: .. dockerfile: .devcontainer/Dockerfile - args: - IMAGE: ${IMAGE} - VERSION: ${VERSION} volumes: - ..:/workspace/sentry:cached working_dir: /workspace/sentry @@ -14,14 +11,13 @@ services: sentry-dev: <<: *sentry-build - entrypoint: ".devcontainer/run --service dev" - command: "sleep infinity" + entrypoint: [".devcontainer/run"] depends_on: - redis sentry-test: <<: *sentry-build - entrypoint: ".devcontainer/run --service test" + entrypoint: [".devcontainer/run"] command: ["foreman", "start"] ports: - "${SENTRY_E2E_RAILS_APP_PORT}:4000" diff --git a/.devcontainer/run b/.devcontainer/run index 59661dace..9cac53c07 100755 --- a/.devcontainer/run +++ b/.devcontainer/run @@ -1,92 +1,54 @@ #!/bin/bash - -set -e +# +# Container entrypoint. +# +# Order of operations: +# 1. Set up mise (the Ruby version manager) for this shell. +# 2. Ensure the desired Ruby is installed (precompiled, no source build). +# 3. Hand off to whatever docker-compose passes as `command`. +# +set -euo pipefail cd /workspace/sentry -sudo mkdir -p vendor/gems -sudo chown -R sentry:sentry vendor/gems - -# git config --global --replace-all safe.directory /workspace/sentry -# git config --global --replace-all safe.directory /workspace/sentry/vendor/gems/* - -sudo chown -R sentry:sentry . - -run_service_setup() { - local service="$1" +# ---- 1. mise setup ----------------------------------------------------------- - echo "๐Ÿš€ Running setup for service: $service" +MISE_BIN="/home/sentry/.local/bin/mise" - case "$service" in - "dev") - if ! .devcontainer/setup --with-foreman --only-bundle; then - echo "โŒ Setup failed for service: $service" - exit 1 - fi - ;; - "test") - if ! .devcontainer/setup --with-foreman --only .,spec/apps/rails-mini; then - echo "โŒ Setup failed for service: $service" - exit 1 - fi - ;; - *) - echo "โŒ Unknown service: $service" - echo "Available services: dev, test" - exit 1 - ;; - esac - - echo "โœ… Setup completed for service: $service" -} +if [[ ! -x "$MISE_BIN" ]]; then + echo "โŒ mise not found at $MISE_BIN (it should be installed by the Dockerfile)" + exit 1 +fi -# Function to start services in background -start_services_if_needed() { - # Check if we're running tests (bundle exec rake) - if [[ "$*" == *"bundle exec rake"* ]]; then - echo "๐Ÿš€ Starting e2e services in background for test execution..." +# Activate mise for this shell so PATH/shims are resolved correctly. +eval "$("$MISE_BIN" activate bash)" - # Start foreman in background - foreman start & - FOREMAN_PID=$! +# Trust the workspace config so mise will use it without prompting. +"$MISE_BIN" trust /workspace/sentry >/dev/null - # Wait for services to be ready - echo "โณ Waiting for services to start..." - for i in {1..30}; do - if curl -f http://localhost:4000/health >/dev/null 2>&1 && \ - curl -f http://localhost:4001/health >/dev/null 2>&1; then - echo "โœ… Services are ready!" - break - fi +# ---- 2. Install Ruby (precompiled) ------------------------------------------- - if [ $i -eq 30 ]; then - echo "โŒ Services failed to start within timeout" - kill $FOREMAN_PID 2>/dev/null || true - exit 1 - fi +# Default to the latest Ruby unless the caller pins a specific version. +RUBY_VERSION="${RUBY_VERSION:-latest}" - sleep 2 - done +# Always pull a precompiled binary from jdx/ruby โ€” never build from source. +export MISE_RUBY_COMPILE=0 - # Set up cleanup trap - trap "echo '๐Ÿงน Stopping services...'; kill $FOREMAN_PID 2>/dev/null || true; wait $FOREMAN_PID 2>/dev/null || true" EXIT - fi -} +# Skip installation when the version is already present in the image (e.g. CI +# images built with the RUBY_VERSION build arg in the Dockerfile). +if "$MISE_BIN" list ruby 2>/dev/null | grep -qF "${RUBY_VERSION}"; then + echo "โœ… ruby@${RUBY_VERSION} already installed, skipping download." +else + echo "๐Ÿ“ฆ Installing ruby@${RUBY_VERSION} (precompiled)..." + "$MISE_BIN" install "ruby@${RUBY_VERSION}" +fi -# Parse arguments -if [ "$1" = "--service" ] && [ -n "$2" ]; then - service="$2" - shift 2 +"$MISE_BIN" use --global "ruby@${RUBY_VERSION}" - run_service_setup "$service" +# ---- 3. Hand off ------------------------------------------------------------- - if [ $# -gt 0 ]; then - start_services_if_needed "$@" - exec "$@" - else - exec bash - fi +if [[ $# -eq 0 ]]; then + exec sleep infinity else - start_services_if_needed "$@" exec "$@" fi diff --git a/.devcontainer/setup b/.devcontainer/setup index 9789d999f..521841f0d 100755 --- a/.devcontainer/setup +++ b/.devcontainer/setup @@ -35,7 +35,6 @@ class SetupScript if should_run_bundle? cleanup_ruby_lsp_directories - update_rubygems_and_bundler install_bundle_dependencies install_foreman_gem if @options[:with_foreman] end @@ -139,22 +138,9 @@ class SetupScript def update_rubygems_and_bundler puts "๐Ÿ“ฆ Updating RubyGems and Bundler..." - if RUBY_VERSION >= "3.0" - unless system("sudo gem update --system --silent") - puts "โŒ RubyGems update failed" - exit 1 - end - else - unless system("sudo gem update --silent --system 3.4.22") - puts "โŒ RubyGems update failed" - exit 1 - end - - # sentry-sidekiq does not bundle with Bundler 2.5.x that ships with RubyGems 3.4.22 - unless system("sudo gem install bundler -v 2.4.22") - puts "โŒ Bundler installation failed" - exit 1 - end + unless system("gem update --system --silent") + puts "โŒ RubyGems update failed" + exit 1 end end diff --git a/.github/workflows/build_images.yml b/.github/workflows/build_images.yml index 0a07303ea..4d05c2f1e 100644 --- a/.github/workflows/build_images.yml +++ b/.github/workflows/build_images.yml @@ -9,11 +9,11 @@ on: - .github/workflows/build_images.yml # Uncomment if you want to test things out in a PR - # - # pull_request: - # paths: - # - .devcontainer/** - # - .github/workflows/build_images.yml + + pull_request: + paths: + - .devcontainer/** + - .github/workflows/build_images.yml permissions: contents: read @@ -24,45 +24,28 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - ruby_image: - - ruby:2.7.8-slim-bullseye - - ruby:3.0.7-slim-bullseye - - ruby:3.1.7-slim-bookworm - - ruby:3.2.9-slim-trixie - - ruby:3.3.10-slim-trixie - - ruby:3.4.8-slim-trixie - - ruby:4.0.0-slim-trixie + ruby_version: + - "2.7.8" + - "3.0.7" + - "3.1.7" + - "3.2.9" + - "3.3.10" + - "3.4.8" + - "4.0.0" steps: - name: Check out current commit uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - name: Generate short image name and extract version + - name: Generate short image name id: image_name run: | - ruby_image="${{ matrix.ruby_image }}" - - # Extract full version for GEM_HOME (e.g., ruby:3.4.5-slim-bookworm -> 3.4.5) - if [[ "$ruby_image" == ruby:* ]]; then - full_version=$(echo "$ruby_image" | cut -d: -f2 | cut -d- -f1) - version=$(echo "$ruby_image" | cut -d: -f2 | cut -d. -f1,2) - short_name="sentry-ruby-devcontainer-${version}" - elif [[ "$ruby_image" == jruby:latest ]]; then - full_version="latest" - short_name="sentry-ruby-devcontainer-jruby-latest" - elif [[ "$ruby_image" == jruby:* ]]; then - full_version=$(echo "$ruby_image" | cut -d: -f2 | cut -d- -f1) - version=$(echo "$ruby_image" | cut -d: -f2 | cut -d. -f1,2) - short_name="sentry-ruby-devcontainer-jruby-${version}" - else - full_version="latest" - short_name="sentry-ruby-devcontainer-${ruby_image}" - fi + ruby_version="${{ matrix.ruby_version }}" + version=$(echo "$ruby_version" | cut -d. -f1,2) + short_name="sentry-ruby-devcontainer-${version}" echo "short_name=${short_name}" >> $GITHUB_OUTPUT - echo "full_version=${full_version}" >> $GITHUB_OUTPUT echo "Generated short image name: ${short_name}" - echo "Extracted full version: ${full_version}" - name: Build and push devcontainer image id: build @@ -73,8 +56,7 @@ jobs: ghcr: true publish_on_pr: true build_args: | - IMAGE=${{ matrix.ruby_image }} - VERSION=${{ steps.image_name.outputs.full_version }} + RUBY_VERSION=${{ matrix.ruby_version }} - name: Use outputs run: | diff --git a/.mise.toml b/.mise.toml new file mode 100644 index 000000000..eefa30b4c --- /dev/null +++ b/.mise.toml @@ -0,0 +1,2 @@ +[tools] +ruby = "latest"