Skip to content

Add proposal and update schema #571

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
170 changes: 170 additions & 0 deletions proposals/add-array-object-feature-option-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
# Proposal: Add `array` and `object` Types to Feature Options Schema

## Problem Statement

The current Dev Container Feature Options Schema lacks support for `array` and `object` types. This limitation hinders the ability to configure features that require lists of values (e.g., dependencies, ports) or key-value mappings (e.g., environment variables, custom settings). Addressing this limitation will enhance the flexibility and usability of the schema.

This proposal aims to address [Issue #567](https://github.com/devcontainers/spec/issues/567).

## Proposed Solution

Extend the Dev Container Feature Options Schema by introducing `array` and `object` as valid types for feature options. These new types will enable developers to create more complex and customizable features.
Copy link
Contributor

Choose a reason for hiding this comment

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

What types of values would be supported? Maybe limiting it to strings and booleans would keep it simple and aligned with the existing types.


### Schema Changes

1. **Array Type**:
- Represents a list of values (e.g., strings, numbers, booleans, or objects).
- Supports optional constraints such as allowed item types and default values.

2. **Object Type**:
- Represents a mapping of key-value pairs.
- Allows nested structures and supports default values for specific keys.

### Example Feature Using `array` and `object`

Below are two examples of how `array` and `object` types can be used in feature configurations.

#### Example 1: Environment Variables

**Feature Definition (`example-feature/devcontainer-feature.json`):**

```json
{
"name": "example-feature",
"id": "example",
"description": "A feature that demonstrates the use of array and object types.",
"options": {
"dependencies": {
"type": "array",
"description": "List of software packages to install",
"default": ["git", "curl", "nodejs"]
},
"envVariables": {
"type": "object",
"description": "Environment variables for the container",
"default": {
"NODE_ENV": "development",
"APP_PORT": "3000"
}
}
},
"install": {
"script": "./install.sh",
"args": []
}
}
```

**Installation Script (`example-feature/install.sh`):**

```bash
#!/bin/bash

# Install dependencies
echo "Installing dependencies..."
for package in "${DEPENDENCIES[@]}"; do
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems to be Bash specific syntax. How would this work with the Bourne shell /bin/sh? The Bourne shell is used for the install scripts to allow for compatibility with distributions not including Bash by default like Alpine Linux.

sudo apt-get install -y "$package"
done

# Configure environment variables
echo "Setting up environment variables..."
for key in "${!ENVVARIABLES[@]}"; do
export "$key=${ENVVARIABLES[$key]}"
done

echo "Feature setup complete."
```

#### Example 2: HTTP Headers

**Feature Definition (`example-feature/devcontainer-feature.json`):**

```json
{
"name": "example-feature",
"id": "example-http",
"description": "A feature that demonstrates the use of HTTP headers in a configuration.",
"options": {
"httpHeaders": {
"type": "object",
"description": "Custom HTTP headers for the development environment",
"default": {
"Authorization": "Bearer example-token",
"Content-Type": "application/json",
"X-Custom-Header": "DevContainerFeature"
}
}
},
"install": {
"script": "./install-http.sh",
"args": []
}
}
```

**Installation Script (`example-feature/install-http.sh`):**

```bash
#!/bin/bash

# Apply HTTP headers
echo "Setting up HTTP headers..."
for key in "${!HTTPHEADERS[@]}"; do
echo "Header: $key -> Value: ${HTTPHEADERS[$key]}"
# Example usage: Write headers to a configuration file
echo "$key: ${HTTPHEADERS[$key]}" >> ./http-headers-config.txt
done

echo "Feature setup complete."
```

### User Configuration Example

Below is an example of how a user can configure these features in their `devcontainer.json` file:

```json
{
"features": {
"ghcr.io/devcontainers/features/example:1": {
"dependencies": ["python3", "pip", "make"],
"envVariables": {
"NODE_ENV": "production",
"APP_PORT": "8080",
"DEBUG": "true"
}
},
"ghcr.io/devcontainers/features/example-http:1": {
"httpHeaders": {
"Authorization": "Bearer user-provided-token",
"X-Custom-Header": "CustomValue",
"Accept-Language": "en-US"
}
}
}
}
```

### Implementation Steps

1. Modify the JSON schema to support the `array` and `object` types for feature options.
2. Add validation logic to ensure proper usage of the new types.
3. Update documentation to include guidance on defining and using `array` and `object` types.
4. Test and validate the functionality with various feature configurations.

## Impact and Risks

### Benefits

1. Enhances the flexibility of feature configurations, enabling developers to define richer and more powerful features.
2. Simplifies user workflows by reducing the need for workarounds involving concatenated strings, triple escaping quotes or other hacks.

### Risks

1. Increased complexity in validation logic for feature options.
2. Potential learning curve for users unfamiliar with JSON schema concepts.

## Acceptance Criteria

1. `array` and `object` types are supported in the Feature Options Schema.
2. Example features using these types are implemented and published.
3. Comprehensive tests and documentation are provided for the new functionality.
54 changes: 51 additions & 3 deletions schemas/devContainerFeature.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@
"type": "string"
},
"type": {
"description": "The type of the option. Can be 'boolean' or 'string'. Options of type 'string' should use the 'enum' or 'proposals' property to provide a list of allowed values.",
"description": "The type of the option. Can be 'boolean', 'string', 'array', or 'object'. Options of type 'string' should use the 'enum' or 'proposals' property to provide a list of allowed values.",
"const": "boolean",
"type": "string"
}
Expand Down Expand Up @@ -267,7 +267,7 @@
"type": "array"
},
"type": {
"description": "The type of the option. Can be 'boolean' or 'string'. Options of type 'string' should use the 'enum' or 'proposals' property to provide a list of allowed values.",
"description": "The type of the option. Can be 'boolean', 'string', 'array', or 'object'. Options of type 'string' should use the 'enum' or 'proposals' property to provide a list of allowed values.",
"const": "string",
"type": "string"
}
Expand Down Expand Up @@ -298,7 +298,7 @@
"type": "array"
},
"type": {
"description": "The type of the option. Can be 'boolean' or 'string'. Options of type 'string' should use the 'enum' or 'proposals' property to provide a list of allowed values.",
"description": "The type of the option. Can be 'boolean', 'string', 'array', or 'object'.. Options of type 'string' should use the 'enum' or 'proposals' property to provide a list of allowed values.",
"const": "string",
"type": "string"
}
Expand All @@ -308,6 +308,54 @@
"default"
],
"type": "object"
},
{
"description": "Option value is represented with an array.",
"additionalProperties": false,
"properties": {
"default": {
"description": "Default value if the user omits this option from their configuration.",
"type": "array"
},
"description": {
"description": "A description of the option displayed to the user by a supporting tool.",
"type": "string"
},
"type": {
"description": "The type of the option. Can be 'boolean', 'string', 'array', or 'object'. Options of type 'string' should use the 'enum' or 'proposals' property to provide a list of allowed values.",
"const": "array",
"type": "string"
}
},
"required": [
"type",
"default"
],
"type": "object"
},
{
"description": "Option value is represented with an object.",
"additionalProperties": false,
"properties": {
"default": {
"description": "Default value if the user omits this option from their configuration.",
"type": "object"
},
"description": {
"description": "A description of the option displayed to the user by a supporting tool.",
"type": "string"
},
"type": {
"description": "The type of the option. Can be 'boolean', 'string', 'array', or 'object'. Options of type 'string' should use the 'enum' or 'proposals' property to provide a list of allowed values.",
"const": "object",
"type": "string"
}
},
"required": [
"type",
"default"
],
"type": "object"
}
]
},
Expand Down