-
Notifications
You must be signed in to change notification settings - Fork 451
Dev container Templates & distribution #98
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
Merged
samruddhikhandale
merged 14 commits into
main
from
samruddhikhandale/templates-proposal
Sep 21, 2022
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
d1de91c
draft
samruddhikhandale 325045a
spec proposal
samruddhikhandale 7abcce4
add option resolution
samruddhikhandale e7e0006
nit
samruddhikhandale f2f1690
address comments
samruddhikhandale 285de32
Option resolution example improvement
samruddhikhandale 8d9f2aa
added clarification
samruddhikhandale a8a33c2
spelling correction
samruddhikhandale 7835eea
address comment
samruddhikhandale 14b507a
adds clarifications
samruddhikhandale 6667cbf
address comments
samruddhikhandale bf8752a
address comments
samruddhikhandale 081d86b
adds back 'type'
samruddhikhandale 44c75b8
adds back semver
samruddhikhandale File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| # Distribution and Discovery of Dev Container Templates | ||
|
|
||
| This specification defines a pattern where community members and organizations can author and self-publish [Dev Container Templates](./devcontainer-templates.md). | ||
|
|
||
| Goals include: | ||
|
|
||
| - For Template authors, create a "self-service" way to publish a Template, either publicly or privately, that is not centrally controlled. | ||
| - Provide the ability to standardize publishing such that supporting tools may implement their own mechanism to aid Template discoverability as they see fit. | ||
|
|
||
| ## Source code | ||
|
|
||
| A Template's source code is stored in a git repository. | ||
|
|
||
| For ease of authorship and maintenance, [1..n] Templates can share a single git repository. This set of Templates is referred to as a "collection," and will share the same [`devcontainer-collection.json`](#devcontainer-collection.json) file and "namespace" (eg. `<owner>/<repo>`). | ||
|
|
||
| > **Note:** Templates and [Features](./devcontainer-features.md) should be placed in different git repositories. | ||
|
|
||
| Source code for a set of Templates follows the example file structure below: | ||
|
|
||
| ``` | ||
| . | ||
| ├── README.md | ||
| ├── src | ||
| │ ├── dotnet | ||
| │ │ ├── devcontainer-template.json | ||
| │ │ ├── .devcontainer.json | ||
| │ │ ├── ... | ||
| | ├ | ||
| │ ├── docker-from-docker | ||
| │ │ ├── devcontainer-template.json | ||
| │ │ ├── .devcontainer | ||
| │ │ ├── devcontainer.json | ||
| │ │ ├── Dockerfile | ||
| │ │ └── ... | ||
| │ │ ├── ... | ||
| | ├ | ||
| │ ├── go-postgres | ||
| │ │ ├── devcontainer-template.json | ||
| │ │ ├── .devcontainer | ||
| │ │ ├── devcontainer.json | ||
| │ │ ├── docker-compose.yml | ||
| │ │ ├── Dockerfile | ||
| │ │ └── ... | ||
| │ │ ├── ... | ||
| ``` | ||
|
|
||
| ...where `src` is a directory containing a sub-folder with the name of the Template (e.g. `src/dotnet` or `src/docker-from-docker`) with at least a file named `devcontainer-template.json` that contains the Template metadata, and a `.devcontainer.json` (or `.devcontainer/devcontainer.json`) that the supporting tools will drop into an existing project or folder. | ||
|
|
||
| Each sub-directory should be named such that it matches the `id` field of the `devcontainer-template.json`. Other files can also be included in the Templates's sub-directory, and will be included during the [packaging step](#packaging) alongside the two required files. Any files that are not part of the Templates's sub-directory (e.g. outside of `src/dotnet`) will not included in the [packaging step](#packaging). | ||
|
|
||
| ## Versioning | ||
|
|
||
| Each Template is individually [versioned according to the semver specification](https://semver.org/). The `version` property in the respective `devcontainer-template.json` file is parsed to determine if the Template should be republished. | ||
|
|
||
| Tooling that handles publishing Templates will not republish Templates if that exact version has already been published; however, tooling must republish major and minor versions in accordance with the semver specification. | ||
|
|
||
| ## Packaging | ||
|
|
||
| Templates are distributed as tarballs. The tarball contains the entire contents of the Template sub-directory, including the `devcontainer-template.json`, `.devcontainer.json` (or `.devcontainer/devcontainer.json`), and any other files in the directory. | ||
|
|
||
| The tarball is named `devcontainer-template-<id>.tgz`, where `<id>` is the Templates's `id` field. | ||
|
|
||
| A reference implementation for packaging and distributing Templates is provided as a GitHub Action (https://github.com/devcontainers/action). | ||
|
|
||
| ### devcontainer-collection.json | ||
|
|
||
| The `devcontainer-collection.json` is an auto-generated metadata file. | ||
|
|
||
| | Property | Type | Description | | ||
| | :--- | :--- | :--- | | ||
| | `sourceInformation` | object | Metadata from the implementing packaging tool. | | ||
| | `templates` | array | The list of Templates that are contained in this collection.| | ||
|
|
||
| Each Template's `devcontainer-template.json` metadata file is appended into the `templates` top-level array. | ||
|
|
||
| ## Distribution | ||
|
|
||
| There are several supported ways to distribute Templates. Distribution is handled by the implementing packaging tool. | ||
|
|
||
| A user can add a Template in to their projects as defined by the [supporting Tools](../docs/specs/supporting-tools.md#supporting-tools-and-services). | ||
|
|
||
| ### OCI Registry | ||
|
|
||
| An OCI registry that implements the [OCI Artifact Distribution Specification](https://github.com/opencontainers/distribution-spec) serves as the primary distribution mechanism for Templates. | ||
|
|
||
| Each packaged Template is pushed to the registry following the naming convention `<registry>/<namespace>/<id>[:version]`, where version is the major, minor, and patch version of the Template, according to the semver specification. | ||
|
|
||
| > **Note:** The `namespace` is a unique identifier for the collection of Templates and must be different than the collection of [Features](./devcontainer-features.md). There are no strict rules for the `namespace`; however, one pattern is to set `namespace` equal to source repository's `<owner>/<repo>`. | ||
|
|
||
| A custom media type `application/vnd.devcontainers` and `application/vnd.devcontainers.layer.v1+tar` are used as demonstrated below. | ||
|
|
||
| For example, the `go` Template in the `devcontainers/templates` namespace at version `1.2.3` would be pushed to the ghcr.io OCI registry. | ||
|
|
||
| > **Note:** The example below uses [`oras`](https://oras.land/) for demonstration purposes. A supporting tool should directly implement the required functionality from the aforementioned OCI artifact distribution specification. | ||
|
|
||
| ```bash | ||
| # ghcr.io/devcontainers/templates/go:1 | ||
| REGISTRY=ghcr.io | ||
| NAMESPACE=devcontainers/templates | ||
| TEMPLATE=go | ||
|
|
||
| ARTIFACT_PATH=devcontainer-template-go.tgz | ||
|
|
||
| for VERSION in 1 1.2 1.2.3 latest | ||
| do | ||
| oras push ${REGISTRY}/${NAMESPACE}/${TEMPLATE}:${VERSION} \ | ||
| --manifest-config /dev/null:application/vnd.devcontainers \ | ||
| ./${ARTIFACT_PATH}:application/vnd.devcontainers.layer.v1+tar | ||
| done | ||
|
|
||
| ``` | ||
|
|
||
| The "namespace" is the globally identifiable name for the collection of Templates. (eg: `owner/repo` for the source code's git repository). | ||
|
|
||
| The auto-generated `devcontainer-collection.json` is pushed to the registry with the same `namespace` as above and no accompanying `template` name. The collection file is always tagged as `latest`. | ||
|
|
||
| ```bash | ||
| # ghcr.io/devcontainers/templates | ||
| REGISTRY=ghcr.io | ||
| NAMESPACE=devcontainers/templates | ||
|
|
||
| oras push ${REGISTRY}/${NAMESPACE}:latest \ | ||
| --manifest-config /dev/null:application/vnd.devcontainers \ | ||
| ./devcontainer-collection.json:application/vnd.devcontainers.collection.layer.v1+json | ||
| ``` | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,186 @@ | ||
| # Dev Container Templates reference | ||
|
|
||
| Development container "Templates" are source files packaged together that encode configuration for a complete development environment. A Template can be used in a new or existing project, and a [supporting tool](https://containers.dev/supporting) will use the configuration from the Template to build a development container. | ||
|
|
||
| The configuration is placed in a [`.devcontainer.json`](/docs/specs/devcontainer-reference.md#devcontainerjson) which can also reference other files within the Template. Alternatively, `.devcontainer/devcontainer.json` can also be used if the container needs to reference other files, such as a `Dockerfile` or `docker-compose.yml`. A Template can also provide additional source files (eg: boilerplate code or a [lifecycle script](https://containers.dev/implementors/json_reference/#lifecycle-scripts). | ||
|
|
||
| Template metadata is captured by a `devcontainer-template.json` file in the root folder of the Template. | ||
|
|
||
| ## Folder structure | ||
|
|
||
| A single Template is a folder with at least a `devcontainer-template.json` and [`devcontainer.json`](/docs/specs/devcontainer-reference.md#devcontainerjson). Additional files are permitted and are packaged along side the required files. | ||
|
|
||
| ``` | ||
| +-- template | ||
| | +-- devcontainer-template.json | ||
| | +-- .devcontainer.json | ||
| | +-- (other files) | ||
| ``` | ||
|
|
||
| ## devcontainer-template.json properties | ||
samruddhikhandale marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| The `devcontainer-template.json` file defines information about the Template to be used by any [supporting tools](../docs/specs/supporting-tools.md#supporting-tools-and-services). | ||
|
|
||
| The properties of the file are as follows: | ||
|
|
||
| | Property | Type | Description | | ||
| | :--- | :--- | :--- | | ||
| | `id` | string | ID of the Template. The `id` should be unique in the context of the repository/published package where the Template exists and must match the name of the directory where the `devcontainer-template.json` resides. | | ||
| | `name` | string | Name of the Template. | | ||
| | `description` | string | Description of the Template. | | ||
| | `documentationURL` | string | Url that points to the documentation of the Template. | | ||
| | `licenseURL` | string | Url that points to the license of the Template. | | ||
| | `type` | string | Type of the dev container (image, dockerfile, dockerCompose) created by the Template. | | ||
samruddhikhandale marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| | `options` | object | A map of options that the supporting tools should use to populate different configuration options for the Template. | | ||
| | `platforms` | object | Languages and platforms supported by the Template. | | ||
| | `publisher` | object | Name of the publisher/maintainer of the Template. | | ||
| | `keywords` | array | List of strings relevant to a user that would search for this Template. | | ||
|
|
||
| ### The `options` property | ||
|
|
||
| The `options` property contains a map of option IDs and their related configuration settings. These `options` are used by the supporting tools to prompt the user to choose from different Template configuration options. The tools would replace the option ID with the selected value in the specified `replaceIn` files. This replacement would happen before dropping the `.devcontainer.json` (or `.devcontainer/devcontainer.json`) and other files (within the sub-directory of the Template) required to containerize your project. See [option resolution](#option-resolution) for more details. For example: | ||
|
|
||
| ```json | ||
| { | ||
| "options": { | ||
| "optionId": { | ||
| "type": "string", | ||
| "description": "Description of the option", | ||
| "proposals": ["value1", "value2"], | ||
| "default": "value1", | ||
| "replaceIn": [".devcontainer.json"], | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| | Property | Type | Description | | ||
| | :--- | :--- | :--- | | ||
| | `optionId` | string | ID of the option used by the supporting tools to replace the selected value in the specified `replaceIn` files. | | ||
| | `optionId.type` | string | Type of the option. Valid types are currently: `boolean`, `string` | | ||
chrmarti marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| | `optionId.description` | string | Description for the option. | | ||
| | `optionId.proposals` | array | A list of suggested string values. Free-form values **are** allowed. Omit when using `optionId.enum`. | | ||
| | `optionId.enum` | array | A strict list of allowed string values. Free-form values are **not** allowed. Omit when using `optionId.proposals`. | | ||
| | `optionId.default` | string | Default value for the option. | | ||
| | `optionId.replaceIn` | array | List of file paths which the supporting tool should use to perform the string replacement of `optionId` with the selected value. The provided path is always relative to the sub-directory of the Template. | | ||
|
|
||
| > `Note`: The `options` must be unique for every `devcontainer-template.json` | ||
|
|
||
| ### Referencing a Template | ||
|
|
||
| The `id` format (`<oci-registry>/<namespace>/<template>[:<semantic-version>]`) dictates how a [supporting tool](https://containers.dev/supporting) will locate and download a given Template from an OCI registry. For example: | ||
|
|
||
| - `ghcr.io/user/repo/go` | ||
| - `ghcr.io/user/repo/go:1` | ||
| - `ghcr.io/user/repo/go:latest` | ||
|
|
||
| The registry must implement the [OCI Artifact Distribution Specification](https://github.com/opencontainers/distribution-spec). Some implementors can be [found here](https://oras.land/implementors/). | ||
|
|
||
| ## Versioning | ||
|
|
||
| Each Template is individually [versioned according to the semver specification](https://semver.org/). The `version` property in the respective `devcontainer-template.json` file is updated to increment the Template's version. | ||
|
|
||
| Tooling that handles releasing Templates will not republish Templates if that exact version has already been published; however, tooling must republish major and minor versions in accordance with the semver specification. | ||
|
|
||
| ## Release | ||
|
|
||
| _For information on distributing Templates, see [devcontainer-templates-distribution.md](./devcontainer-templates-distribution.md)._ | ||
|
|
||
| ### Option Resolution | ||
|
|
||
| A Template's `options` property is used by a supporting tool to prompt for different configuration options. A supporting tool will parse the `options` object provided by the user. If a value is selected for a Template, it will be replaced in the provided `replaceIn` files. | ||
|
|
||
| ### Option resolution example | ||
|
|
||
| Consider a `java` Template with the following folder structure: | ||
|
|
||
| +-- java | ||
| | +-- devcontainer-template.json | ||
| | +-- .devcontainer.json | ||
|
|
||
| Suppose the `java` Template has the following `options` parameters declared in the `devcontainer-template.json` file: | ||
|
|
||
| ```json | ||
| // ... | ||
| "options": { | ||
| "imageVariant": { | ||
| "type": "string", | ||
| "description": "Specify version of java.", | ||
| "proposals": [ | ||
| "17-bullseye", | ||
| "17-buster", | ||
| "11-bullseye", | ||
| "11-buster", | ||
| "17", | ||
| "11" | ||
| ], | ||
| "default": "17-bullseye", | ||
| "replaceIn": [".devcontainer.json"] | ||
| }, | ||
| "nodeVersion": { | ||
| "type": "string", | ||
| "proposals": [ | ||
| "latest", | ||
| "16", | ||
| "14", | ||
| "10", | ||
| "none" | ||
| ], | ||
| "default": "latest", | ||
| "description": "Specify version of node, or 'none' to skip node installation.", | ||
| "replaceIn": [".devcontainer.json"] | ||
| }, | ||
| "installMaven": { | ||
| "type": "boolean", | ||
| "description": "Install Maven, a management tool for Java.", | ||
| "default": "false", | ||
| "replaceIn": [".devcontainer.json"] | ||
| }, | ||
| } | ||
| ``` | ||
|
|
||
| and it has the following `.devcontainer.json` file: | ||
|
|
||
| ```json | ||
| { | ||
| "name": "Java", | ||
| "image": "mcr.microsoft.com/devcontainers/java:0-${templateOption:imageVariant}", | ||
| "features": { | ||
| "ghcr.io/devcontainers/features/node:1": { | ||
| "version": "${templateOption:nodeVersion}", | ||
| "installMaven": "${templateOption:installMaven}" | ||
| } | ||
| }, | ||
| // ... | ||
| } | ||
| ``` | ||
|
|
||
| A user tries to add the `java` Template to their project using the [supporting tools](../docs/specs/supporting-tools.md#supporting-tools-and-services) and selects `17-bullseye` when prompted for `"Specify version of Go"` and the `default` values when prompted for `"Specify version of node, or 'none' to skip node installation"` and `"Install Maven, a management tool for Java"`. | ||
|
|
||
| The supporting tool could then use a string replacer (eg. [handlebars](https://handlebarsjs.com/)) for the files mentioned by `replaceIn` property. In this example, `.devcontainer.json` needs to be modified and hence, the inputs can provided to it as follows: | ||
|
|
||
| ``` | ||
| { | ||
| imageVariant:"17-bullseye", | ||
| nodeVersion: "latest", | ||
| installMaven: "false" | ||
| } | ||
| ``` | ||
|
|
||
| The modified `.devcontainer.json` will be as follows: | ||
|
|
||
| ```json | ||
| { | ||
| "name": "Go", | ||
| "image": "mcr.microsoft.com/devcontainers/go:0-17-bullseye", | ||
| "features": { | ||
| "ghcr.io/devcontainers/features/node:1": { | ||
| "version": "latest", | ||
| "installMaven": "false" | ||
| } | ||
| }, | ||
| ... | ||
| } | ||
| ``` | ||
|
|
||
| The modified `.devcontainer.json` would be dropped into any existing folder as a starting point for containerizing your project. | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.