SERVER-112730: Clean up and simplify logic for devcontainers and ensu… (#43253)

GitOrigin-RevId: efc2c8b17d75118a19be04f55400f4058937c44e
This commit is contained in:
Eric Lavigne 2025-10-30 10:30:09 -06:00 committed by MongoDB Bot
parent 7f012eba7c
commit 06e4e46688
6 changed files with 274 additions and 212 deletions

View File

@ -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

View File

@ -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"

View File

@ -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"
}

View File

@ -1,6 +0,0 @@
#!/usr/bin/env bash
USER=$(_CONTAINER_USER)
cat ./setup.sh >/workspace-setup.sh
sudo chmod a+x /workspace-setup.sh

View File

@ -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 <<BLOCK
if [[ -f ~/.bashrc ]]; then
source ~/.bashrc
fi
BLOCK
)
idem_file_append ~/.bash_profile "Source .bashrc" "$block"
set +o nounset
source ~/.bash_profile
set -o nounset
echo "Finished setting up ~/.bashprofile..."
}
setup_mongo_venv() {
echo "################################################################################"
echo "Setting up the local virtual environment..."
# PYTHON_KEYRING_BACKEND is needed to make poetry install work
# See guide https://wiki.corp.mongodb.com/display/KERNEL/Virtual+Workstation
export PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring
/opt/mongodbtoolchain/v4/bin/python3 -m venv python3-venv
source ./python3-venv/bin/activate
POETRY_VIRTUALENVS_IN_PROJECT=true poetry install --no-root --sync
deactivate
echo "Finished setting up the local virtual environment..."
echo "Activate it by running 'source python3-venv/bin/activate'"
}
setup_poetry() {
echo "################################################################################"
echo "Installing 'poetry' command..."
export PATH="$PATH:$HOME/.local/bin"
if command -v poetry &>/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

215
.devcontainer/post-create.sh Executable file
View File

@ -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}" <<EOF
# Auto-activate MongoDB Python virtual environment
if [ -f "${WORKSPACE_FOLDER}/python3-venv/bin/activate" ]; then
source "${WORKSPACE_FOLDER}/python3-venv/bin/activate"
fi
EOF
echo "[OK] Python venv activation added to ${shell_name}"
else
echo "Info: Python venv activation already configured in ${shell_name}"
fi
fi
}
if [ -f "${WORKSPACE_FOLDER}/python3-venv/bin/activate" ]; then
configure_shell_venv "${HOME}/.bashrc" ".bashrc"
configure_shell_venv "${HOME}/.zshrc" ".zshrc"
else
echo "Warning: Python virtual environment not found at ${WORKSPACE_FOLDER}/python3-venv"
fi
# Step 8: Sync Python dependencies
echo "Syncing Python dependencies..."
if [ -f "${WORKSPACE_FOLDER}/python3-venv/bin/activate" ]; then
# Only run sync if venv was just created OR if we're updating an existing one
if [ "${venv_created}" = true ]; then
echo "Info: Skipping sync for newly created venv (already installed via poetry)"
else
source "${WORKSPACE_FOLDER}/python3-venv/bin/activate"
if "${WORKSPACE_FOLDER}/buildscripts/poetry_sync.sh"; then
echo "[OK] Python dependencies synced successfully"
else
echo "Warning: Failed to sync Python dependencies"
fi
deactivate
fi
else
echo "Warning: Python virtual environment not found at ${WORKSPACE_FOLDER}/python3-venv"
fi
# Step 9: Install Node modules
echo "Installing Node.js dependencies..."
if bazel run @pnpm//:pnpm --config=local -- --dir "${WORKSPACE_FOLDER}" install; then
echo "[OK] Node.js dependencies installed successfully"
else
echo "Error: Failed to install Node.js dependencies"
exit 1
fi
echo ""
echo "MongoDB development container setup completed successfully!"
echo "Info: All infrastructure and dependencies are now configured and up to date."
echo "Info: VS Code Python extension automatically handles virtual environment activation."
echo "Info: You can now start developing with MongoDB!"