Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"recommendations": [
"davidanson.vscode-markdownlint",
"ms-azuretools.vscode-containers",
"redhat.vscode-yaml",
"streetsidesoftware.code-spell-checker",
"redhat.vscode-yaml"
]
}
11 changes: 3 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# VS Code Dev Container Playground

This repository provides a template-based workflow for VS Code Dev Containers. Shared settings live under `compose-base/`, and each local dev container is created by copying `projects/.template/` into `projects/`.
This repository provides a template-based workflow for VS Code Dev Containers. Each local dev container is created by copying `projects/.template/` into `projects/`.

## Getting Started

Expand All @@ -9,11 +9,6 @@ Prerequisites: Docker is available on the host, and VS Code has the `ms-vscode-r
1. Download the repository (e.g., `git clone https://github.com/iplaylf2/vscode-container.git`).
2. Copy `projects/.template/` to `projects/<your-project-name>`.
3. Edit `projects/<your-project-name>/docker-compose.yaml` as needed.
4. Set `VSCODE_CONTAINER_PROJECT` in the host environment that will launch VS Code.
5. Open `projects/<your-project-name>` in VS Code and run `Dev Containers: Reopen in Container`.
4. Open `projects/<your-project-name>` in VS Code and run `Dev Containers: Reopen in Container`.

After the container is created, VS Code opens the workspace at `/mnt/${VSCODE_CONTAINER_PROJECT}`. The data lives in a Docker volume named `${VSCODE_CONTAINER_PROJECT}`.

## Configuration Notes

- `VSCODE_CONTAINER_PROJECT` provides the project identity (volume name and `/mnt/<project>` path); it is an environment variable because `devcontainer.json` cannot pass variables into Docker Compose.
After the container is created, VS Code opens the workspace at `/mnt/<your-project-name>`. The data lives in a Docker volume named `<your-project-name>_workspace`.
12 changes: 0 additions & 12 deletions compose-base/docker-compose.yaml

This file was deleted.

13 changes: 13 additions & 0 deletions features/fnm/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"id": "fnm",
"version": "1.0.0",
"name": "Fast Node Manager (fnm)",
"description": "Installs fnm and optionally a Node.js version.",
"options": {
"nodeVersion": {
"type": "string",
"default": "lts",
"description": "Node.js version to install with fnm. Use 'none' to skip."
}
}
}
Comment on lines +1 to +13
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Well-structured manifest. Consider adding optional metadata.

The feature manifest is correctly structured with appropriate options that align with the install script. The nodeVersion option properly defaults to "lts" and clearly documents the "none" skip behavior.

📝 Optional: Add additional metadata

Consider enriching the manifest with optional fields for better discoverability:

 {
   "id": "fnm",
   "version": "1.0.0",
   "name": "Fast Node Manager (fnm)",
   "description": "Installs fnm and optionally a Node.js version.",
+  "documentationURL": "https://github.com/Schniz/fnm",
+  "licenseURL": "https://github.com/Schniz/fnm/blob/master/LICENSE",
   "options": {
     "nodeVersion": {
       "type": "string",
       "default": "lts",
       "description": "Node.js version to install with fnm. Use 'none' to skip."
     }
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{
"id": "fnm",
"version": "1.0.0",
"name": "Fast Node Manager (fnm)",
"description": "Installs fnm and optionally a Node.js version.",
"options": {
"nodeVersion": {
"type": "string",
"default": "lts",
"description": "Node.js version to install with fnm. Use 'none' to skip."
}
}
}
{
"id": "fnm",
"version": "1.0.0",
"name": "Fast Node Manager (fnm)",
"description": "Installs fnm and optionally a Node.js version.",
"documentationURL": "https://github.com/Schniz/fnm",
"licenseURL": "https://github.com/Schniz/fnm/blob/master/LICENSE",
"options": {
"nodeVersion": {
"type": "string",
"default": "lts",
"description": "Node.js version to install with fnm. Use 'none' to skip."
}
}
}
🤖 Prompt for AI Agents
In @features/fnm/devcontainer-feature.json around lines 1 - 13, The manifest for
the fnm feature is valid but could include optional metadata to improve
discoverability; add fields such as "author", "homepage", "tags" (array of
strings), and "license" at the top level alongside "id" and "version" so
consumers can surface the feature better, and ensure the existing
"options.nodeVersion" (default "lts", description unchanged) remains intact.

20 changes: 20 additions & 0 deletions features/fnm/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
if [ -z "${BASH_VERSION:-}" ] || [ "$(id -un)" != "$_REMOTE_USER" ]; then
exec su "$_REMOTE_USER" -c "bash -lc '$0'"
fi

set -euo pipefail

curl -fsSL https://fnm.vercel.app/install | bash

FNM_PATH="$HOME/.local/share/fnm"
export PATH="$FNM_PATH:$PATH"
Comment on lines +10 to +11
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Missing fnm shell initialization for persistent activation.

While the PATH is set for the current session, fnm requires shell initialization to work correctly across sessions. The installation script should configure shell startup files to automatically initialize fnm.

🔧 Add fnm shell initialization

After line 11, add shell initialization to the user's shell configuration:

 FNM_PATH="$HOME/.local/share/fnm"
 export PATH="$FNM_PATH:$PATH"
+
+# Initialize fnm in shell startup files
+cat >> "$HOME/.bashrc" <<'EOF'
+
+# fnm
+export FNM_PATH="$HOME/.local/share/fnm"
+export PATH="$FNM_PATH:$PATH"
+eval "$(fnm env)"
+EOF

This ensures fnm is available in all future shell sessions and enables features like automatic version switching.

🤖 Prompt for AI Agents
In @features/fnm/install.sh around lines 10 - 11, The script only sets FNM_PATH
and PATH for the current session; after those assignments, append persistent
shell initialization so fnm is automatically initialized in future sessions:
detect common shells and add the appropriate fnm initialization snippet to each
shell’s startup file (for example, add the bash/zsh init line and the fish init
line) so that invoking fnm/env happens on new shells; update the install script
(around the FNM_PATH and PATH assignments) to write or idempotently append those
init lines to the user’s shell rc files and ensure you don’t duplicate entries
on repeated runs.


NODE_VERSION="${NODE_VERSION:-lts}"
if [ "$NODE_VERSION" != "none" ]; then
if [ "$NODE_VERSION" = "lts" ]; then
fnm install --lts
else
fnm install "$NODE_VERSION"
fi
fi
13 changes: 13 additions & 0 deletions features/uv/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"id": "uv",
"version": "1.0.0",
"name": "uv",
"description": "Installs uv and optionally a Python version.",
"options": {
"pythonVersion": {
"type": "string",
"default": "3",
"description": "Python version to install with uv. Use 'none' to skip."
}
}
}
Comment on lines +1 to +13
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Well-structured manifest. Consider adding optional metadata.

The feature manifest is correctly structured with a pythonVersion option that aligns with the install script. The default value of "3" and "none" skip behavior are clearly documented.

📝 Optional: Add additional metadata

Consider enriching the manifest with optional fields:

 {
   "id": "uv",
   "version": "1.0.0",
   "name": "uv",
   "description": "Installs uv and optionally a Python version.",
+  "documentationURL": "https://docs.astral.sh/uv/",
+  "licenseURL": "https://github.com/astral-sh/uv/blob/main/LICENSE-MIT",
   "options": {
     "pythonVersion": {
       "type": "string",
       "default": "3",
       "description": "Python version to install with uv. Use 'none' to skip."
     }
   }
 }
🤖 Prompt for AI Agents
In @features/uv/devcontainer-feature.json around lines 1 - 13, Add optional
metadata fields to the feature manifest to improve discoverability and
maintainability: extend the top-level object that already contains "id",
"version", "name", "description", and "options" by adding keys such as
"displayName", "categories" (array), "maintainers" (array of objects with
name/email), "license", "repository" (url), and optional "tags" and
"postAttachCommand" so consumers can see human-friendly name, authorship,
licensing and source without changing the existing "pythonVersion" option.

14 changes: 14 additions & 0 deletions features/uv/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env bash
if [ -z "${BASH_VERSION:-}" ] || [ "$(id -un)" != "$_REMOTE_USER" ]; then
exec su "$_REMOTE_USER" -c "bash -lc '$0'"
fi

set -euo pipefail

curl -fsSL https://astral.sh/uv/install.sh | sh
. "$HOME/.local/bin/env"

PYTHON_VERSION="${PYTHON_VERSION:-3}"
if [ "$PYTHON_VERSION" != "none" ]; then
uv python install --default "$PYTHON_VERSION"
fi
16 changes: 8 additions & 8 deletions projects/.template/.devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "${localEnv:VSCODE_CONTAINER_PROJECT}",
"dockerComposeFile": [
"../../../compose-base/docker-compose.yaml",
"../docker-compose.yaml"
"dockerComposeFile": "../docker-compose.yaml",
"mounts": [
"source=workspace,target=${containerWorkspaceFolder},type=volume"
],
"name": "${localWorkspaceFolderBasename}",
"overrideCommand": true,
"postCreateCommand": "sudo chown $(id -un) ${containerWorkspaceFolder}",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider enhancing the ownership change command.

The postCreateCommand only changes the owner of the workspace directory itself, not its contents. If files are already present in the workspace volume, they will retain their original ownership.

♻️ Suggested enhancement

Consider adding the -R flag for recursive ownership change and including the group:

-	"postCreateCommand": "sudo chown $(id -un) ${containerWorkspaceFolder}",
+	"postCreateCommand": "sudo chown -R $(id -un):$(id -gn) ${containerWorkspaceFolder}",

This ensures all files in the workspace have the correct ownership and group membership.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"postCreateCommand": "sudo chown $(id -un) ${containerWorkspaceFolder}",
"postCreateCommand": "sudo chown -R $(id -un):$(id -gn) ${containerWorkspaceFolder}",
🤖 Prompt for AI Agents
In @projects/.template/.devcontainer/devcontainer.json at line 8, The
postCreateCommand currently only changes the ownership of the workspace
directory itself; update the "postCreateCommand" to perform a recursive,
owner-and-group change so all files under ${containerWorkspaceFolder} are
chowned correctly (use chown -R and include $(id -gn) or :$(id -gn) to set the
group) and ensure you still use $(id -un) for the user to preserve the original
intent.

"service": "devcontainer",
"workspaceFolder": "/mnt/${localEnv:VSCODE_CONTAINER_PROJECT}",
"postCreateCommand": "sudo chown vscode ${containerWorkspaceFolder}",
"overrideCommand": true
}
"workspaceFolder": "/mnt/${localWorkspaceFolderBasename}"
}
3 changes: 1 addition & 2 deletions projects/.template/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
services:
devcontainer:
# Base settings come from ../../compose-base/docker-compose.yaml via dockerComposeFile.
build:
# Pick the Dockerfile that matches your dev environment.
dockerfile: images/base/Dockerfile
dockerfile: ../../images/base/Dockerfile
args:
# Optional: toolchain version for Dockerfiles that accept SDK_VERSION.
SDK_VERSION: 0
Loading