devcontainers / spec

Development Containers: Use a container as a full-featured development environment.
https://containers.dev
Creative Commons Attribution 4.0 International
3.38k stars 207 forks source link

Addition of "$ref" property to enable flexible configuration composition #23

Open greggroth opened 2 years ago

greggroth commented 2 years ago

Problem

Multiple teams collaborating on a common codebase may have different dependency or setup needs and currently this is done by sharing a single devcontainer.json configuration with all of their individual needs combined. https://github.com/microsoft/dev-container-spec/issues/6 describes support for multiple configuration files, but there isn't set a way to consolidate the shared configuration into a single file.

A simpler approach to solving this problem is proposed in https://github.com/microsoft/dev-container-spec/issues/22 while this aims to be a more complete solution. However, it's unclear if this level of support is truly necessary.

Proposed Solution

Taking influence from JSON Schema and the Open API 3.0 specs, a special property named "$ref" would allow importing all or part of a referenced document. Here is an example:

Given a file "defaults.json":

// .devcontainer/defaults.json
{
    "name": "microsoft/foo",
    "extensions": [
        "/root/hello-githug.vsix"
    ],
    "forwardPorts": [80, 5432]
    "hostRequirements": {
        "storage": "64gb",
        "memory": "32gb"
    },
    "portsAttributes": {
        "80": {
            "label": "web"
        },
        "5432": {
            "label": "postgres"
        }
    }
}

If a team would like to introduce a team-specific configuration that adds ports for Redis, they can add a new configuration:

// .devcontainer/redis-team.json
{
    "$ref": "defaults", 
    "extensions": [],
    "forwardPorts": [ { "$ref": "defaults#/forwardPorts" }, 6379], // Explicit merge behavior
    "hostRequirements": {
        "memory": "64gb"    // Require more memory
    },
    "portAttributes": {
        "$ref": "defaults#/portAttributes",                        // Explicit merge behavior
        "6379": {
            "label": "redis"
        }
    }
}

Resulting in a final configuration:

{
    "name": "microsoft/foo",
    "extensions": [],               
    "forwardPorts": [80, 5432, 6379],
    "hostRequirements": {
        "storage": "64gb",
        "memory": "64gb"
    },
    "portAttributes": {
        "80": {
            "label": "web"
        },
        "5432": {
            "label": "postgres"
        }
        "6379": {
            "label": "redis"
        }
    }
}

Allowed Values for $ref

The value of a $ref contains a file and optionally a JSON Pointer path separated by a "#"

See here for more examples of JSON Pointer paths.

Remote Reference:

URL Reference:

bamurtaugh commented 2 years ago

How does this experience differ from https://github.com/microsoft/dev-container-spec/issues/22? Both seem to allow referencing / building off of another devcontainer.json, perhaps with this proposal allowing for referencing a specific property rather than entire file.

greggroth commented 2 years ago

They solve basically the same problem, but in different ways. #22 uses a single top-level "extends" keyword to a file that is treated as a "parent" document with while this approach is more flexible, but consequently more complicated. I opened up separate issues for them since they aren't incompatible with one another.