From 06e4e466888f174bc923cce266a544028e609a0b Mon Sep 17 00:00:00 2001 From: Eric Lavigne Date: Thu, 30 Oct 2025 10:30:09 -0600 Subject: [PATCH] =?UTF-8?q?SERVER-112730:=20Clean=20up=20and=20simplify=20?= =?UTF-8?q?logic=20for=20devcontainers=20and=20ensu=E2=80=A6=20(#43253)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GitOrigin-RevId: efc2c8b17d75118a19be04f55400f4058937c44e --- .devcontainer/Dockerfile | 52 ++++- .devcontainer/devcontainer.json | 23 +- .../workstation/devcontainer-feature.json | 17 -- .devcontainer/features/workstation/install.sh | 6 - .devcontainer/features/workstation/setup.sh | 173 -------------- .devcontainer/post-create.sh | 215 ++++++++++++++++++ 6 files changed, 274 insertions(+), 212 deletions(-) delete mode 100644 .devcontainer/features/workstation/devcontainer-feature.json delete mode 100755 .devcontainer/features/workstation/install.sh delete mode 100644 .devcontainer/features/workstation/setup.sh create mode 100755 .devcontainer/post-create.sh diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 2505fd9c161..413a0f82108 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -10,7 +10,22 @@ ARG USER_GID=$USER_UID # Create the user RUN groupadd $USERNAME && useradd -s /bin/bash --gid $USER_GID -m $USERNAME -RUN apt-get update && apt-get install -y sudo curl ca-certificates xdg-utils +RUN apt-get update && apt-get install -y \ + sudo \ + curl \ + ca-certificates \ + xdg-utils \ + wget \ + less \ + jq \ + vim-tiny \ + procps \ + lsof \ + zip \ + unzip \ + openssh-client \ + git \ + && rm -rf /var/lib/apt/lists/* # Install xdg-open wrapper for browser integration COPY .devcontainer/xdg-open-wrapper.sh /usr/local/bin/xdg-open-wrapper.sh @@ -20,8 +35,9 @@ RUN chmod +x /usr/local/bin/xdg-open-wrapper.sh && \ fi && \ ln -s /usr/local/bin/xdg-open-wrapper.sh /usr/bin/xdg-open -# Give user sudo access -RUN echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/devcontaineruser && chmod 0440 /etc/sudoers.d/devcontaineruser +# Give user sudo access (common-utils feature will enhance this) +RUN echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/devcontaineruser && \ + chmod 0440 /etc/sudoers.d/devcontaineruser # Persistent bash history RUN SNIPPET="export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" \ @@ -67,7 +83,37 @@ RUN /opt/mongodbtoolchain/revisions/*/scripts/install.sh; echo "Toolchain instal # Add MongoDB toolchain to PATH ENV PATH="/opt/mongodbtoolchain/v5/bin:${PATH}" +# Setup bash profile to source .bashrc +RUN echo "" >> "$HOME/.bash_profile" && \ + echo "# BEGIN Source .bashrc" >> "$HOME/.bash_profile" && \ + echo "if [[ -f ~/.bashrc ]]; then" >> "$HOME/.bash_profile" && \ + echo " source ~/.bashrc" >> "$HOME/.bash_profile" && \ + echo "fi" >> "$HOME/.bash_profile" && \ + echo "# END Source .bashrc" >> "$HOME/.bash_profile" + +# Create MongoDB data directory +USER root +RUN mkdir -p /data/db && chown -R ${USERNAME}:${USERNAME} /data/db +USER $USERNAME + # Bazel telemetry RUN echo "common --bes_keywords=devcontainer:use=true" >> "$HOME/.bazelrc" && \ echo "common --bes_keywords=devcontainer:image=$BASE_IMAGE" >> "$HOME/.bazelrc" && \ echo "common --bes_keywords=devcontainer:username=$USERNAME" >> "$HOME/.bazelrc" + +# Install pipx (Python package manager for tools) +ENV PATH="/home/${USERNAME}/.local/bin:${PATH}" +RUN /opt/mongodbtoolchain/v5/bin/python3 -m venv /tmp/pipx-venv && \ + /tmp/pipx-venv/bin/python -m pip install --upgrade "pip<20.3" && \ + /tmp/pipx-venv/bin/python -m pip install pipx && \ + /tmp/pipx-venv/bin/pipx install pipx --python /opt/mongodbtoolchain/v5/bin/python3 --force && \ + /tmp/pipx-venv/bin/pipx ensurepath --force && \ + rm -rf /tmp/pipx-venv + +# Install db-contrib-tool using pipx +RUN /home/${USERNAME}/.local/bin/pipx install db-contrib-tool + +# Install poetry with pinned dependencies +COPY --chown=${USERNAME}:${USERNAME} poetry_requirements.txt /tmp/poetry_requirements.txt +RUN /home/${USERNAME}/.local/bin/pipx install poetry --pip-args="-r /tmp/poetry_requirements.txt" && \ + rm /tmp/poetry_requirements.txt diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index b35a2050f35..26545ff0b31 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -25,6 +25,11 @@ "source": "mongo-bashhistory", "target": "/commandhistory", "type": "volume" + }, + { + "source": "${containerWorkspaceFolderBasename}-python3-venv", + "target": "${containerWorkspaceFolder}/python3-venv", + "type": "volume" } ], "containerEnv": { @@ -33,10 +38,11 @@ "remoteUser": "${localEnv:USER}", "containerUser": "${localEnv:USER}", "features": { - "./features/workstation": {}, "ghcr.io/devcontainers/features/git:1": {}, "ghcr.io/devcontainers-community/features/bazel:1": {}, - "ghcr.io/devcontainers/features/docker-outside-of-docker:1": {}, + "ghcr.io/devcontainers/features/docker-outside-of-docker:1": { + "moby": false + }, "ghcr.io/devcontainers/features/node:1": { "version": "lts" }, @@ -44,16 +50,7 @@ "username": "${localEnv:USER}" } }, - "postCreateCommand": { - "fixVolumePerms": "sudo chown -R $(whoami): ${containerEnv:HOME}/.config/engflow_auth && sudo chown -R $(whoami): ${containerEnv:HOME}/.cache", - "venvActivation": "echo 'source ${containerWorkspaceFolder}/python3-venv/bin/activate && ${containerWorkspaceFolder}/buildscripts/poetry_sync.sh' >> ~/.bashrc && echo 'source ${containerWorkspaceFolder}/python3-venv/bin/activate && ${containerWorkspaceFolder}/buildscripts/poetry_sync.sh' >> ~/.zshrc;", - "createDataDir": "sudo mkdir -p /data/db && sudo chown -R $(whoami): /data/db", - "installNodeModules": "bazel run @pnpm//:pnpm -- --dir ${containerWorkspaceFolder} install", - "reportDockerServerPlatform": "echo \"\ncommon --bes_keywords=devcontainer:docker_server_platform=$(docker version --format '\"{{.Server.Platform.Name}}\"')\" >> ${containerEnv:HOME}/.bazelrc", - "reportDockerServerVersion": "echo \"\ncommon --bes_keywords=devcontainer:docker_server_version=$(docker version --format '\"{{.Server.Version}}\"')\" >> ${containerEnv:HOME}/.bazelrc", - "reportArch": "echo \"\ncommon --bes_keywords=devcontainer:arch=$(uname -i)\" >> ${containerEnv:HOME}/.bazelrc", - "fetchTags": "if [ -f .git/shallow ]; then git fetch --unshallow --tags; else git fetch --tags; fi || true" - }, + "postCreateCommand": "WORKSPACE_FOLDER=${containerWorkspaceFolder} ${containerWorkspaceFolder}/.devcontainer/post-create.sh", "shutdownAction": "none", "portsAttributes": { "*": { @@ -113,7 +110,7 @@ "[starlark]": { "editor.defaultFormatter": "BazelBuild.vscode-bazel" }, - "terminal.integrated.wordSeparators": " ()[]{}',\"`─‘’@", + "terminal.integrated.wordSeparators": " ()[]{}',\"`─''@", "yaml.schemas": { "./buildscripts/idl/idl_schema.yml": [ "*.idl" diff --git a/.devcontainer/features/workstation/devcontainer-feature.json b/.devcontainer/features/workstation/devcontainer-feature.json deleted file mode 100644 index a7e18db116f..00000000000 --- a/.devcontainer/features/workstation/devcontainer-feature.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "id": "workstation-setup", - "version": "1.0.1", - "name": "Workstation Setup", - "description": "Sets up a development workstation environment with essential tools and configurations.", - "installsAfter": [ - "ghcr.io/devcontainers-community/features/bazel" - ], - "mounts": [ - { - "source": "${containerWorkspaceFolderBasename}-python3-venv", - "target": "${containerWorkspaceFolder}/python3-venv", - "type": "volume" - } - ], - "postCreateCommand": "sudo chown -R $(whoami): ${containerWorkspaceFolder}/python3-venv && sudo chown $(whoami): ${containerWorkspaceFolder}/.. && bash /workspace-setup.sh" -} diff --git a/.devcontainer/features/workstation/install.sh b/.devcontainer/features/workstation/install.sh deleted file mode 100755 index 1d68fa1f8e1..00000000000 --- a/.devcontainer/features/workstation/install.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -USER=$(_CONTAINER_USER) - -cat ./setup.sh >/workspace-setup.sh -sudo chmod a+x /workspace-setup.sh diff --git a/.devcontainer/features/workstation/setup.sh b/.devcontainer/features/workstation/setup.sh deleted file mode 100644 index 53bc631e0df..00000000000 --- a/.devcontainer/features/workstation/setup.sh +++ /dev/null @@ -1,173 +0,0 @@ -silent_grep() { - command grep -q "$@" >/dev/null 2>&1 -} - -idem_file_append() { - if [[ -z "$1" ]]; then - return 1 - fi - if [[ ! -f "$1" && -n "${4-}" ]]; then - return - fi - if [[ -z "$2" ]]; then - return 2 - fi - if [[ -z "$3" ]]; then - return 3 - fi - local start_marker="# BEGIN $2" - local end_marker="# END $2" - if ! silent_grep "^$start_marker" "$1"; then - { - echo -e "\n$start_marker" - echo -e "$3" - echo -e "$end_marker" - } >>"$1" - fi -} - -setup_bash() { - # Bash profile should source .bashrc - echo "################################################################################" - echo "Setting up bash..." - local block=$( - cat </dev/null; then - echo "'poetry' command exists; skipping setup" - else - pipx install poetry --pip-args="-r $(pwd)/poetry_requirements.txt" - echo "Finished installing poetry..." - fi -} - -setup_pipx() { - echo "################################################################################" - echo "Installing 'pipx' command..." - if command -v pipx &>/dev/null; then - echo "'pipx' command exists; skipping setup" - else - export PATH="$PATH:$HOME/.local/bin" - local venv_name="tmp-pipx-venv" - /opt/mongodbtoolchain/v4/bin/python3 -m venv $venv_name - - # virtualenv doesn't like nounset - set +o nounset - source $venv_name/bin/activate - set -o nounset - - python -m pip install --upgrade "pip<20.3" - python -m pip install pipx - - pipx install pipx --python /opt/mongodbtoolchain/v4/bin/python3 --force - pipx ensurepath --force - - set +o nounset - deactivate - set -o nounset - - rm -rf $venv_name - - source ~/.bashrc - - echo "Finished installing pipx..." - fi -} - -setup_db_contrib_tool() { - echo "################################################################################" - echo "Installing 'db-contrib-tool' command..." - export PATH="$PATH:$HOME/.local/bin" - if command -v db-contrib-tool &>/dev/null; then - echo "'db-contrib-tool' command exists; skipping setup" - else - pipx install db-contrib-tool - echo "Finished installing db-contrib-tool" - fi -} - -setup_clang_config() { - echo "################################################################################" - echo "Installing clang config..." - - bazel build compiledb --config=local - - echo "Finished installing clang config..." -} - -setup_gdb() { - echo "################################################################################" - echo "Setting up GDB..." - - cwd=$(pwd) - cd .. - if [[ -d 'Boost-Pretty-Printer' ]]; then - echo "'Boost-Pretty-Printer' dir exists; skipping setup" - else - git clone https://github.com/mongodb-forks/Boost-Pretty-Printer.git - - # the original version of this script just appended this line, so we - # have to grep for it manually - if ! silent_grep "source $HOME/gdbinit" ~/.gdbinit; then - idem_file_append ~/.gdbinit "Server Workflow Tool gdbinit" "source $HOME/gdbinit" - fi - - echo "Finished installing pretty printers..." - fi - cd $cwd -} - -run_setup() { - set +o nounset - source ~/.bashrc - set -o nounset - - setup_bash - - setup_clang_config - setup_gdb - setup_pipx - setup_db_contrib_tool # This step requires `setup_pipx` to have been run. - setup_poetry # This step requires `setup_pipx` to have been run. - - setup_mongo_venv # This step requires `setup_poetry` to have been run. - - echo "Please run 'source ~/.bashrc' to complete setup!" -} - -run_setup diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh new file mode 100755 index 00000000000..f47358556fb --- /dev/null +++ b/.devcontainer/post-create.sh @@ -0,0 +1,215 @@ +#!/bin/bash +set -euo pipefail + +# MongoDB Development Container Post-Create Setup Script +# This script handles setup tasks that run when the container is first created, +# including infrastructure setup, developer tooling installation, and workspace +# configuration. Steps are idempotent and safe to re-run. + +echo "Starting MongoDB development container post-create setup..." + +# Step 1: Fix volume permissions +echo "Fixing volume permissions..." +sudo chown -R "$(whoami)": "${HOME}/.config/engflow_auth" || echo "Warning: Could not fix engflow_auth permissions" +sudo chown -R "$(whoami)": "${HOME}/.cache" || echo "Warning: Could not fix cache permissions" +sudo chown -R "$(whoami)": "${WORKSPACE_FOLDER}/python3-venv" || echo "Warning: Could not fix python3-venv permissions" +sudo chown "$(whoami)": "${WORKSPACE_FOLDER}/.." || echo "Warning: Could not fix parent directory permissions" + +# Fix Git repository permissions (prevents "insufficient permission" errors) +echo "Fixing Git repository permissions..." +sudo chown -R "$(whoami)": "${WORKSPACE_FOLDER}/.git" || echo "Warning: Could not fix .git permissions" +echo "[OK] Volume and Git permissions fixed" + +# Step 2: Configure Bazel with Docker information (one-time container setup) +echo "Configuring Bazel with Docker server information..." + +# Helper function to add Bazel keyword if not already present +add_bazel_keyword() { + local keyword="$1" + local value="$2" + local bazelrc="${HOME}/.bazelrc" + + if ! grep -q "devcontainer:${keyword}" "${bazelrc}" 2>/dev/null; then + echo "common --bes_keywords=devcontainer:${keyword}=\"${value}\"" >>"${bazelrc}" + echo "[OK] ${keyword} configured: ${value}" + else + echo "Info: ${keyword} already configured" + fi +} + +# Report Docker server platform +DOCKER_PLATFORM=$(docker version --format '{{.Server.Platform.Name}}' 2>/dev/null || echo "unknown") +add_bazel_keyword "docker_server_platform" "${DOCKER_PLATFORM}" + +# Report Docker server version +DOCKER_VERSION=$(docker version --format '{{.Server.Version}}' 2>/dev/null || echo "unknown") +add_bazel_keyword "docker_server_version" "${DOCKER_VERSION}" + +# Report architecture +ARCH=$(uname -i 2>/dev/null || echo "unknown") +add_bazel_keyword "arch" "${ARCH}" + +# Step 4: Unshallow Git repository (one-time setup) +echo "Setting up Git repository..." + +# Helper function to handle git shallow lock +wait_for_git_lock() { + local lockfile=".git/shallow.lock" + if [ -f "${lockfile}" ]; then + echo "Warning: Git shallow lock detected, waiting for it to clear..." + local count=0 + while [ -f "${lockfile}" ] && [ $count -lt 30 ]; do + sleep 1 + ((count++)) + done + + # If lock still exists after 30 seconds, remove it + if [ -f "${lockfile}" ]; then + echo "Warning: Removing stale git shallow lock..." + rm -f "${lockfile}" + fi + fi +} + +wait_for_git_lock + +# Proceed with git operations +if [ -f .git/shallow ]; then + echo "Info: Detected shallow repository, fetching complete history with tags..." + if git fetch --unshallow --tags; then + echo "[OK] Git repository unshallowed and tags fetched successfully" + else + echo "Warning: Failed to unshallow repository and fetch tags" + echo "Hint: You can manually run: git fetch --unshallow --tags" + fi +else + echo "Info: Repository is already complete, fetching tags..." + if git fetch --tags; then + echo "[OK] Git tags fetched successfully" + else + echo "Warning: Failed to fetch tags" + echo "Hint: You can manually run: git fetch --tags" + fi +fi + +# Step 4: Create Python virtual environment and install dependencies +echo "Creating Python virtual environment..." +venv_created=false +if [ ! -d "${WORKSPACE_FOLDER}/python3-venv/bin" ]; then + export PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring + /opt/mongodbtoolchain/v5/bin/python3 -m venv "${WORKSPACE_FOLDER}/python3-venv" + venv_created=true + + # Install dependencies in the newly created venv + set +o nounset + source "${WORKSPACE_FOLDER}/python3-venv/bin/activate" + set -o nounset + + if command -v poetry &>/dev/null; then + POETRY_VIRTUALENVS_IN_PROJECT=true poetry install --no-root --sync + echo "[OK] Python virtual environment created and dependencies installed" + else + echo "Warning: poetry not available, skipping dependency installation" + fi + + set +o nounset + deactivate + set -o nounset +else + echo "Info: Python virtual environment already exists" +fi + +# Step 5: Build clang configuration +echo "Building clang configuration..." +if bazel build compiledb --config=local --config=no-remote-exec; then + echo "[OK] Clang configuration built successfully" +else + echo "Warning: Failed to build clang configuration" +fi + +# Step 6: Setup GDB pretty printers +echo "Setting up GDB pretty printers..." +cd "${WORKSPACE_FOLDER}/.." +if [ -d "Boost-Pretty-Printer" ]; then + echo "Info: Boost-Pretty-Printer already exists" +else + if git clone https://github.com/mongodb-forks/Boost-Pretty-Printer.git; then + if ! grep -q "source ${HOME}/gdbinit" "${HOME}/.gdbinit" 2>/dev/null; then + echo "" >>"${HOME}/.gdbinit" + echo "# BEGIN Server Workflow Tool gdbinit" >>"${HOME}/.gdbinit" + echo "source ${HOME}/gdbinit" >>"${HOME}/.gdbinit" + echo "# END Server Workflow Tool gdbinit" >>"${HOME}/.gdbinit" + fi + echo "[OK] GDB pretty printers installed" + else + echo "Warning: Failed to clone Boost-Pretty-Printer" + fi +fi +cd "${WORKSPACE_FOLDER}" + +# Step 7: Configure automatic Python virtual environment activation +echo "Configuring automatic Python virtual environment activation..." + +# Helper function to add Python venv activation to shell config +configure_shell_venv() { + local shell_config="$1" + local shell_name="$2" + + if [ -f "${shell_config}" ]; then + if ! grep -q "python3-venv/bin/activate" "${shell_config}" 2>/dev/null; then + cat >>"${shell_config}" <