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
11 changes: 5 additions & 6 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ jobs:
strategy:
matrix:
features:
- color
- hello
- conan
baseImage:
- debian:latest
- ubuntu:latest
- docker.io/library/ubuntu:latest
- docker.io/library/debian:latest
- mcr.microsoft.com/devcontainers/base:ubuntu
- mcr.microsoft.com/devcontainers/base:debian
steps:
- uses: actions/checkout@v4

Expand All @@ -34,8 +34,7 @@ jobs:
strategy:
matrix:
features:
- color
- hello
- conan
steps:
- uses: actions/checkout@v4

Expand Down
84 changes: 84 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Dev Container Features Repo

Template for authoring and publishing [Dev Container Features](https://containers.dev/implementors/features/) to GitHub Container Registry.

## Repository Structure

```
src/<feature>/
devcontainer-feature.json # Feature metadata, options, version
install.sh # Entrypoint, runs as root during build
test/<feature>/
scenarios.json # Test scenarios (optional)
test.sh # Auto-generated tests (runs against defaults)
<scenario_name>.sh # Per-scenario test scripts
test/_global/
scenarios.json # Multi-feature integration tests
```

## Required CLI

Install the devcontainer CLI globally:

```bash
npm install -g @devcontainers/cli
```

## Development Commands

**Validate feature metadata:**
```bash
devcontainer features validate --base-path ./src
```

**Test a single feature (auto-generated):**
```bash
devcontainer features test -f <feature> --skip-scenarios -i mcr.microsoft.com/devcontainers/base:ubuntu .
```

**Test a single feature (scenarios only):**
```bash
devcontainer features test -f <feature> --skip-autogenerated .
```

**Test global scenarios:**
```bash
devcontainer features test --global-scenarios-only .
```

## Feature Implementation Notes

- `install.sh` always runs as **root** during container build
- Options from `devcontainer-feature.json` are passed as **capitalized env vars** (e.g., `favorite` → `FAVORITE`)
- Use `$_REMOTE_USER` and `$_CONTAINER_USER` for user context if needed
- Feature version is defined in `devcontainer-feature.json` (semver)
- Declare dependencies via `installsAfter` in metadata

## CI Workflows

- `.github/workflows/validate.yml`: Validates `devcontainer-feature.json` on PR
- `.github/workflows/test.yaml`: Runs auto-generated + scenario tests on PR/push
- `.github/workflows/release.yaml`: Publishes to GHCR (manual trigger, main branch only)

## Publishing

- Namespace: `ghcr.io/<owner>/<repo>/<feature>:<version>`
- Requires `package:write` permission and GITHUB_TOKEN
- Packages default to **private** in GHCR; must manually set to **public** in package settings
- README auto-generated per feature (merges `src/<feature>/NOTES.md` if exists)

## Test Library

Tests can use the bundled test library:

```bash
source dev-container-features-test-lib
check "<label>" <command> [args...]
reportResults
```

## Resources

- [Feature spec](https://containers.dev/implementors/features/)
- [Test docs](https://github.com/devcontainers/cli/blob/main/docs/features/test.md)
- [Distribution spec](https://containers.dev/implementors/features-distribution/)
24 changes: 24 additions & 0 deletions src/conan/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Conan Package Manager (1.x) (conan)

Installs Conan 1.x, the open source C/C++ package manager

## Example Usage

```json
"features": {
"ghcr.io/devcontainers/feature-starter/conan:1": {
"conanVersion": "latest"
}
}
```

## Options

| Options Id | Description | Type | Default Value |
|-----|-----|-----|-----|
| conanVersion | Version of Conan 1.x to install (e.g., '1.66.0', '1.60.2', or 'latest' for newest 1.x) | string | latest |



---

18 changes: 18 additions & 0 deletions src/conan/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "Conan Package Manager (1.x)",
"id": "conan",
"version": "1.0.0",
"description": "Installs Conan 1.x, the open source C/C++ package manager",
"documentationURL": "https://docs.conan.io/en/1.x/",
"options": {
"conanVersion": {
"type": "string",
"default": "latest",
"description": "Version of Conan 1.x to install (e.g., '1.66.0', '1.60.2', or 'latest' for newest 1.x)"
}
},
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils",
"ghcr.io/devcontainers/features/python"
]
}
126 changes: 126 additions & 0 deletions src/conan/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#!/bin/sh
set -e

echo "Activating feature 'conan' (1.x series only)"

CONAN_VERSION="${CONANVERSION:-latest}"
CONAN_VENV_DIR="/opt/conan-venv"

validate_version() {
if [ "$CONAN_VERSION" = "latest" ]; then
return
fi
MAJOR=$(echo "$CONAN_VERSION" | cut -d. -f1)
if [ "$MAJOR" != "1" ]; then
echo "ERROR: This feature only supports Conan 1.x. Requested version: $CONAN_VERSION"
exit 1
fi
}

get_installed_version() {
if ! command -v conan > /dev/null 2>&1; then
echo ""
return
fi
RAW=$(conan --version 2>&1 | head -n1)
echo "$RAW" | sed -n 's/.*version \([0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/p'
}

check_existing_conan() {
INSTALLED=$(get_installed_version)

if [ -z "$INSTALLED" ]; then
echo "No existing Conan installation found."
return
fi

MAJOR=$(echo "$INSTALLED" | cut -d. -f1)
if [ "$MAJOR" != "1" ]; then
echo "ERROR: Conan $INSTALLED is installed, but this feature requires Conan 1.x"
exit 1
fi

if [ "$CONAN_VERSION" = "latest" ]; then
echo "Conan 1.x already installed ($(conan --version 2>&1 | head -n1))"
echo "Skipping installation."
exit 0
fi

if [ "$INSTALLED" = "$CONAN_VERSION" ]; then
echo "Conan $CONAN_VERSION is already installed. Skipping."
exit 0
fi

echo "ERROR: Version conflict. Requested: $CONAN_VERSION, Installed: $INSTALLED"
exit 1
}

detect_distribution() {
if [ ! -f /etc/os-release ]; then
echo "ERROR: Cannot detect distribution. /etc/os-release not found."
exit 1
fi
. /etc/os-release
echo "$ID"
}

install_python() {
DISTRO=$(detect_distribution)
echo "Detected distribution: $DISTRO"

if command -v python3 > /dev/null 2>&1; then
echo "Python3 is already installed"
return
fi

echo "Installing Python3..."
case "$DISTRO" in
debian|ubuntu)
apt-get update && apt-get install -y --no-install-recommends python3 python3-venv
;;
arch|archlinux)
pacman -Sy --noconfirm python
;;
fedora)
dnf install -y python3
;;
alpine)
apk add --no-cache python3 py3-pip
;;
*)
echo "ERROR: Unsupported distribution: $DISTRO"
exit 1
;;
esac
}

install_conan() {
echo "Creating virtual environment at $CONAN_VENV_DIR..."
python3 -m venv "$CONAN_VENV_DIR"

if [ "$CONAN_VERSION" = "latest" ]; then
echo "Installing latest Conan 1.x..."
"$CONAN_VENV_DIR/bin/pip" install --upgrade "conan<2"
else
echo "Installing Conan $CONAN_VERSION..."
"$CONAN_VENV_DIR/bin/pip" install "conan==$CONAN_VERSION"
fi

ln -sf "$CONAN_VENV_DIR/bin/conan" /usr/local/bin/conan
chmod -R a+rX "$CONAN_VENV_DIR"

if ! command -v conan > /dev/null 2>&1; then
echo "ERROR: Conan installation failed"
exit 1
fi

echo "Successfully installed: $(conan --version 2>&1 | head -n1)"
}

validate_version
check_existing_conan
echo "Installing Conan ${CONAN_VERSION}"
install_python
install_conan

echo "Conan feature activation complete!"
3 changes: 2 additions & 1 deletion test/_global/color_and_hello.sh → test/_global/all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ echo -e "\n"
# The 'check' command comes from the dev-container-features-test-lib.
check "check purple is my favorite color" bash -c "color | grep 'my favorite color is purple'"
check "check I am greeting with 'Greetings'" bash -c "hello | grep 'Greetings, $(whoami)'"

check "conan is installed" command -v conan
check "conan --version" bash -c "conan --version"

# Report result
# If any of the checks above exited with a non-zero exit code, the test will fail.
Expand Down
23 changes: 12 additions & 11 deletions test/_global/scenarios.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
{
"color_and_hello": {
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"features": {
"color": {
"favorite": "purple"
},
"hello": {
"greeting": "Greetings"
}
}
"all": {
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"features": {
"color": {
"favorite": "purple"
},
"hello": {
"greeting": "Greetings"
},
"conan": {}
}
}
}
}
13 changes: 13 additions & 0 deletions test/conan/alpine.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash

# Test scenario: alpine
# Tests Conan installation on Alpine latest

set -e

source dev-container-features-test-lib

check "conan is installed" command -v conan
check "conan --version" bash -c "conan --version"

reportResults
13 changes: 13 additions & 0 deletions test/conan/archlinux.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash

# Test scenario: archlinux
# Tests Conan installation on Arch Linux latest

set -e

source dev-container-features-test-lib

check "conan is installed" command -v conan
check "conan --version" bash -c "conan --version"

reportResults
13 changes: 13 additions & 0 deletions test/conan/fedora.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash

# Test scenario: fedora
# Tests Conan installation on Fedora latest

set -e

source dev-container-features-test-lib

check "'conan' is installed" command -v conan
check "conan version works" bash -c "conan --version"

reportResults
13 changes: 13 additions & 0 deletions test/conan/installed.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash

# Test scenario: specific_version
# Tests installing a specific Conan 1.x version

set -e

source dev-container-features-test-lib

check "conan is installed" command -v conan
check "conan --version" bash -c "conan --version"

reportResults
Loading
Loading