Conversation
…untu in dev container tests
…roving output assertions
There was a problem hiding this comment.
Pull request overview
Adds a reusable Dev Container Feature under devcontainer/ to install apm-cli (plus prerequisites) in containers, along with unit + integration test coverage for the feature.
Changes:
- Introduces a devcontainer feature (
devcontainer-feature.json+install.sh) that installsuv, ensures Python 3.10+ andgit, and installsapm-cliwith a PEP 668 retry path. - Adds Bats unit tests for
install.shand devcontainer CLI integration scenarios across several base images and configuration variants. - Adds supporting docs (
devcontainer/README.md), a local sync helper script, and git submodules for the Bats test framework.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| devcontainer/src/apm/install.sh | Feature install script: uv install, Python/git setup, pip install of apm-cli with PEP 668 retry |
| devcontainer/src/apm/devcontainer-feature.json | Feature manifest and version option + installsAfter ordering |
| devcontainer/test/apm/scenarios.json | Integration test matrix (distros + pinned version + python feature) |
| devcontainer/test/apm/*.sh | Scenario validation scripts + shared generic checks |
| devcontainer/test/apm/unit/install.bats | Stub-based unit tests covering install.sh branches |
| devcontainer/scripts/sync-local-devcontainer.sh | Helper to copy the local feature into .devcontainer/ for VS Code consumption |
| devcontainer/README.md | Feature usage, structure, and test documentation |
| .gitmodules | Adds bats-core, bats-support, bats-assert as submodules |
| mkdir -p .devcontainer | ||
| rm -rf .devcontainer/apm-feature | ||
| cp -R devcontainer/src/apm .devcontainer/apm-feature | ||
|
|
||
| cat > .devcontainer/devcontainer.json <<'EOF' | ||
| { | ||
| "name": "APM Development", | ||
| "image": "mcr.microsoft.com/devcontainers/python:3.12", | ||
| "features": { | ||
| "./apm-feature": {} | ||
| } | ||
| } |
There was a problem hiding this comment.
This helper script writes .devcontainer/devcontainer.json and copies the feature into .devcontainer/apm-feature, but the repo does not appear to gitignore .devcontainer/. Running it will leave untracked files that are easy to accidentally commit. Consider either (a) adding .devcontainer/ to .gitignore, or (b) having the script emit a clear warning / require an explicit flag before overwriting an existing .devcontainer/devcontainer.json.
There was a problem hiding this comment.
Not certain if adding .devcontainer to gitignore works with the repo. Not implementing unless directed otherwise
| cat > .devcontainer/devcontainer.json <<'EOF' | ||
| { | ||
| "name": "APM Development", | ||
| "image": "mcr.microsoft.com/devcontainers/python:3.12", | ||
| "features": { |
There was a problem hiding this comment.
This helper overwrites .devcontainer/devcontainer.json via cat > .... If a contributor already has a local devcontainer config, this will clobber it. Consider failing when the file exists unless --force is passed, or writing to a different filename and printing instructions.
| echo "Installing uv..." | ||
| _uv_tmp="$(mktemp /tmp/uv_install.XXXXXX)" | ||
| trap 'rm -f "$_uv_tmp"' EXIT INT TERM | ||
| curl -LsSf https://astral.sh/uv/install.sh > "$_uv_tmp" | ||
| UV_INSTALL_DIR=/usr/local/bin sh "$_uv_tmp" |
There was a problem hiding this comment.
The uv installation downloads and executes a remote shell script as root (curl https://astral.sh/uv/install.sh then sh), without any integrity verification or pinning. This is a supply-chain risk for devcontainer builds. Consider installing uv via the distro package manager when available, or pinning/verifying the installer (e.g., checksum/signature or a versioned release artifact) before executing it.
| } | ||
| EOF | ||
|
|
||
| echo "Synced .devcontainer/apm-feature from devcontainer/src/apm" |
There was a problem hiding this comment.
This helper script writes .devcontainer/devcontainer.json and copies a local feature into .devcontainer/apm-feature, but .devcontainer/ is not currently ignored by git in this repo. Running it will leave untracked files that are easy to accidentally commit. Consider adding .devcontainer/ to .gitignore (or writing into a clearly-temporary path) and/or printing an explicit note that these files are intended to remain uncommitted.
| echo "Synced .devcontainer/apm-feature from devcontainer/src/apm" | |
| echo "[+] Synced .devcontainer/apm-feature from devcontainer/src/apm" | |
| echo "[!] Generated .devcontainer/ content is for local development only and should remain uncommitted." | |
| if ! git check-ignore -q .devcontainer 2>/dev/null; then | |
| echo "[!] .devcontainer/ is not ignored by git in this repo. Review 'git status' and do not commit these generated files." | |
| fi |
| python3 -m ensurepip --upgrade 2>/dev/null || true | ||
| if command -v pip3 >/dev/null 2>&1; then | ||
| PIP_CMD="pip3" | ||
| elif command -v pip >/dev/null 2>&1; then | ||
| PIP_CMD="pip" | ||
| else | ||
| echo "[x] pip is not available and could not be bootstrapped." >&2 | ||
| exit 1 |
There was a problem hiding this comment.
If a base image already has python3 but does not have pip3/pip on PATH (common on Debian/Ubuntu where ensurepip is often split into python3-venv and pip lives in python3-pip), the script falls through to python3 -m ensurepip and then exits 1 if that module isn't available. To make the feature more robust, consider installing the distro pip package when a package manager is available (apt: python3-pip, apk: py3-pip, dnf: python3-pip) instead of relying solely on ensurepip.
| python3 -m ensurepip --upgrade 2>/dev/null || true | |
| if command -v pip3 >/dev/null 2>&1; then | |
| PIP_CMD="pip3" | |
| elif command -v pip >/dev/null 2>&1; then | |
| PIP_CMD="pip" | |
| else | |
| echo "[x] pip is not available and could not be bootstrapped." >&2 | |
| exit 1 | |
| if command -v apt-get >/dev/null 2>&1; then | |
| apt_update_once | |
| apt-get install -y -qq --no-install-recommends python3-pip | |
| elif command -v apk >/dev/null 2>&1; then | |
| apk add --no-cache py3-pip | |
| elif command -v dnf >/dev/null 2>&1; then | |
| dnf install -y python3-pip | |
| fi | |
| if command -v pip3 >/dev/null 2>&1; then | |
| PIP_CMD="pip3" | |
| elif command -v pip >/dev/null 2>&1; then | |
| PIP_CMD="pip" | |
| else | |
| python3 -m ensurepip --upgrade 2>/dev/null || true | |
| if command -v pip3 >/dev/null 2>&1; then | |
| PIP_CMD="pip3" | |
| elif command -v pip >/dev/null 2>&1; then | |
| PIP_CMD="pip" | |
| else | |
| echo "[x] pip is not available and could not be bootstrapped." >&2 | |
| exit 1 | |
| fi |
Description
Packages
apm-clias a reusable Dev Container Feature underdevcontainer/.The feature installs
uv, Python 3.10+,git, andapm-cliinside the container. It supports aversionoption (latestor semver), declaresinstallsAfter: ghcr.io/devcontainers/features/pythonfor correct ordering, and handles the PEP 668--break-system-packagesretry on Ubuntu 24.04.Also included: 37 bats unit tests covering every branch of
install.sh(stub-based, no Docker needed), and a full integration test matrix across Ubuntu 24.04, Ubuntu 22.04, Debian 12, Alpine 3.20, Fedora 41, and a pinned-version + Python-feature scenario.See
devcontainer/README.mdfor more infoFixes #717
Type of change
Testing
Unit tests (no Docker required):
cd devcontainer/test/apm/unit ../../bats/bin/bats install.batsIntegration tests (requires Docker and
@devcontainers/cli):devcontainer features test \ --features apm \ --skip-autogenerated \ --project-folder devcontainer