spec icon indicating copy to clipboard operation
spec copied to clipboard

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

Open greggroth opened this issue 3 years ago • 2 comments

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 "#"

  • Import an entire document: "default", "ports.json",
  • Import the "forwardPorts" value: "defaults.json#/forwardPorts"

See here for more examples of JSON Pointer paths.

Remote Reference:

  • File in the same directory, extension optional: "document"
  • File in the same directory with extension: "document.json"
  • File in a subdirectory: "defaults/ports.json"
  • File in a parent directory: "../other/defaults.json"

URL Reference:

  • File at a URI: "https://github.com/microsoft/foo/blob/master/.devcontainer/devcontainer.json"
  • File at a URI: "https://example.com/configuration.json"

greggroth avatar Mar 25 '22 20:03 greggroth

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.

bamurtaugh avatar Mar 30 '22 17:03 bamurtaugh

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.

greggroth avatar Mar 30 '22 18:03 greggroth