├── CODEOWNERS
├── docs
├── images
│ ├── README.md
│ ├── Slack_Mark-24x24.png
│ ├── so-image-24x24.png
│ ├── gitter-icon-24x24.png
│ └── Twitter_Social_Icon_24x24.png
├── specs
│ ├── parallel-lifecycle-script-execution.md
│ ├── features-user-env-variables.md
│ ├── gpu-host-requirement.md
│ ├── secrets-support.md
│ ├── declarative-secrets.md
│ ├── devcontainer-lockfile.md
│ ├── devcontainer-id-variable.md
│ ├── features-contribute-lifecycle-scripts.md
│ ├── image-metadata.md
│ ├── features-legacyIds-deprecated-properties.md
│ ├── devcontainer-templates-distribution.md
│ ├── supporting-tools.md
│ ├── devcontainer-templates.md
│ ├── devcontainer-features-distribution.md
│ ├── feature-dependencies.md
│ └── devcontainer-reference.md
└── README.md
├── images
└── dev-container-stages.png
├── CODE_OF_CONDUCT.md
├── schemas
├── devContainer.schema.json
├── devContainerFeature.schema.json
└── devContainer.base.schema.json
├── proposals
├── README.md
├── build-options.md
└── features-library.md
├── LICENSE-CODE
├── SECURITY.md
├── README.md
├── CONTRIBUTING.md
├── .gitignore
└── LICENSE
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @devcontainers/maintainers
2 |
--------------------------------------------------------------------------------
/docs/images/README.md:
--------------------------------------------------------------------------------
1 | # Images
2 |
3 | These images are used in READMEs.
4 |
--------------------------------------------------------------------------------
/docs/images/Slack_Mark-24x24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devcontainers/spec/HEAD/docs/images/Slack_Mark-24x24.png
--------------------------------------------------------------------------------
/docs/images/so-image-24x24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devcontainers/spec/HEAD/docs/images/so-image-24x24.png
--------------------------------------------------------------------------------
/images/dev-container-stages.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devcontainers/spec/HEAD/images/dev-container-stages.png
--------------------------------------------------------------------------------
/docs/images/gitter-icon-24x24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devcontainers/spec/HEAD/docs/images/gitter-icon-24x24.png
--------------------------------------------------------------------------------
/docs/images/Twitter_Social_Icon_24x24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devcontainers/spec/HEAD/docs/images/Twitter_Social_Icon_24x24.png
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Microsoft Open Source Code of Conduct
2 |
3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
4 |
5 | Resources:
6 |
7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
10 |
--------------------------------------------------------------------------------
/schemas/devContainer.schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "allOf": [
3 | {
4 | "$ref": "./devContainer.base.schema.json"
5 | },
6 | {
7 | "$ref": "https://raw.githubusercontent.com/microsoft/vscode/main/extensions/configuration-editing/schemas/devContainer.codespaces.schema.json"
8 | },
9 | {
10 | "$ref": "https://raw.githubusercontent.com/microsoft/vscode/main/extensions/configuration-editing/schemas/devContainer.vscode.schema.json"
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/proposals/README.md:
--------------------------------------------------------------------------------
1 | # Proposals
2 |
3 | The files in this folder detail active proposals to the Dev Container Specification.
4 |
5 | We add any proposal to the [**proposals folder**](https://github.com/devcontainers/spec/tree/main/proposals), only if the proposal is accepted. It stays in that folder until the implementation is in progress. Once code/schema changes are released, we move it into the [**specs folder**](https://github.com/devcontainers/spec/tree/main/docs/specs).
6 |
7 | > **Note:** Our [contributing.md](/contributing.md) documents guidelines on contributing to the spec, as well as labels (proposal and finalization) defined for this process for GitHub issues.
8 |
--------------------------------------------------------------------------------
/LICENSE-CODE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Microsoft Corporation.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE
22 |
--------------------------------------------------------------------------------
/docs/specs/parallel-lifecycle-script-execution.md:
--------------------------------------------------------------------------------
1 | # Parallel Lifecycle Script Execution
2 |
3 | This has now been implemented:
4 | * Discussion issue: https://github.com/devcontainers/spec/issues/83
5 | * CLI PR: https://github.com/devcontainers/cli/pull/172
6 |
7 | Below is the original proposal.
8 |
9 | ## Goal
10 |
11 | Support executing multiple lifecycle scripts in parallel by providing them in `object` form.
12 |
13 | ## Motivation
14 |
15 | Dev containers support a single command for each of their lifecycle scripts. While serial execution of multiple commands can be achieved with `;`, `&&`, etc. parallel is less straightforward and so deserves first-class support.
16 |
17 | ## Spec changes
18 |
19 | All lifecycle scripts will be extended to support `object` types. The key of the `object` will be a unique name for the command and the value will be the `string` or `array` command. Each command must exit successfully for the stage to be considered successful.
20 |
21 | Each entry in the `object` will be run in parallel during that lifecycle step.
22 |
23 | ### Example
24 |
25 | ```json
26 | {
27 | "postCreateCommand": {
28 | "server": "npm start",
29 | "db": ["mysql", "-u", "root", "-p", "my database"]
30 | }
31 | }
32 | ```
33 |
--------------------------------------------------------------------------------
/docs/specs/features-user-env-variables.md:
--------------------------------------------------------------------------------
1 | # User Env Variables for Features
2 |
3 | This has now been implemented:
4 | * Discussion issue: https://github.com/devcontainers/spec/issues/91
5 | * CLI PR: https://github.com/devcontainers/cli/pull/228
6 |
7 | Below is the original proposal.
8 |
9 | ## Goal
10 |
11 | Feature scripts run as the `root` user and sometimes need to know which user account the dev container will be used with.
12 |
13 | (The dev container user can be configured through the `remoteUser` property in the devcontainer.json. If that is not set, the container user will be used.)
14 |
15 | ## Proposal
16 |
17 | Pass `_REMOTE_USER` and `_CONTAINER_USER` environment variables to the features scripts with `_CONTAINER_USER` being the container's user and `_REMOTE_USER` being the configured `remoteUser`. If no `remoteUser` is configured, `_REMOTE_USER` is set to the same value as `_CONTAINER_USER`.
18 |
19 | Additionally the home folders of the two users are passed to the feature scripts as `_REMOTE_USER_HOME` and `_CONTAINER_USER_HOME` environment variables.
20 |
21 | ## Notes
22 |
23 | - The container user can be set with `containerUser` in the devcontainer.json and image metadata, `user` in the docker-compose.yml, `USER` in the Dockerfile and can be passed down from the base image.
24 |
--------------------------------------------------------------------------------
/proposals/build-options.md:
--------------------------------------------------------------------------------
1 | # [Proposal] Allow additional cli options to pass to the build command
2 |
3 | Related to:
4 | - https://github.com/devcontainers/spec/discussions/301
5 | - https://github.com/devcontainers/spec/pull/324
6 | - https://github.com/microsoft/vscode-remote-release/issues/3545
7 |
8 | ## Goal
9 |
10 | Extend the build section of the configuration with an option to pass additional arguments via cli.
11 |
12 | ## Proposal
13 |
14 | Introduce the property `options` to the `build` property in the schema as type `Array (string)`. This field contains elements that are directly passed as cli-arguments to the `build` command. It is similar to `runArgs` property which contains cli-arguments passed to the `run` command.
15 |
16 | ### Execution
17 |
18 | When a dev container is built, all entries from the `options` are passed to the `build` command after all other options.
19 |
20 | ## Examples
21 |
22 | ### Add a host
23 |
24 | The following example adds a host-definition while the image is being built:
25 |
26 | ```jsonc
27 | {
28 | "build": {
29 | "dockerfile": "Dockerfile",
30 | "options": [
31 | "--add-host=host.docker.internal:host-gateway"
32 | ]
33 | },
34 | }
35 | ```
36 |
37 | ### Use host network
38 |
39 | The following example allows to use the host network while building an image:
40 |
41 | ```jsonc
42 | {
43 | "build": {
44 | "dockerfile": "Dockerfile",
45 | "options": [
46 | "--network=host"
47 | ]
48 | },
49 | }
50 | ```
51 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Specification Documentation
2 |
3 | The files in this folder detail the Dev Container Specification.
4 |
5 | We add any proposal to the [**proposals folder**](https://github.com/devcontainers/spec/tree/main/proposals), only if the proposal is accepted. It stays in that folder until the implementation is in progress. Once code/schema changes are released, we move it into the [**specs folder**](https://github.com/devcontainers/spec/tree/main/docs/specs).
6 |
7 | > **Note:** Our [contributing.md](/contributing.md) documents guidelines on contributing to the spec, as well as labels (proposal and finalization) defined for this process for GitHub issues.
8 |
9 | There are several main specs in this folder, outlining the key top-level behavior of dev containers, Templates, and Features:
10 |
11 | * [Main dev container spec](/docs/specs/devcontainer-reference.md)
12 | * [Dev container metadata reference](/docs/specs/devcontainerjson-reference.md)
13 | * [Main Templates spec](/docs/specs/devcontainer-templates.md)
14 | * [Templates distribution spec](/docs/specs/devcontainer-templates-distribution.md)
15 | * [Main Features spec](/docs/specs/devcontainer-features.md)
16 | * [Features distribution spec](/docs/specs/devcontainer-features-distribution.md)
17 | * [Tools that support the spec](/docs/specs/supporting-tools.md)
18 |
19 | The remainder of the files in this folder outline specifications for specific dev container behaviors. Each file includes links to when the proposal was first discussed and when it was merged into the Dev Container CLI or schema.
20 |
--------------------------------------------------------------------------------
/docs/specs/gpu-host-requirement.md:
--------------------------------------------------------------------------------
1 | # GPU Host Requirement
2 |
3 | This has now been implemented:
4 | * Discussion issue: https://github.com/devcontainers/spec/issues/82
5 | * CLI PR: https://github.com/devcontainers/cli/pull/173
6 |
7 | Below is the original proposal.
8 |
9 | ## Goal
10 |
11 | This proposal adds GPU support to the existing host requirements properties.
12 | - Issue: https://github.com/devcontainers/spec/issues/82
13 | - Community PR: https://github.com/devcontainers/cli/pull/173
14 |
15 | ## Proposal
16 |
17 | We propose to add a new `gpu` property to the `hostRequirements` object in the devcontainer.json schema. The property can be a boolean value, the string `optional` or an object:
18 |
19 | (Additional row for the existing table in the spec.)
20 | | Property | Type | Description |
21 | |----------|------|-------------|
22 | | `hostRequirements.gpu` 🏷️ | boolean \| 'optional' | Indicates whether or not a GPU is required. A value 'optional' indicates that might be used if available. The object is described in the table below. The default is false. For example: `"hostRequirements": "optional"` |
23 |
24 | The object value can have the following properties:
25 |
26 | | Property | Type | Description |
27 | |----------|------|-------------|
28 | | `hostRequirements.gpu.cores` 🏷️ | integer | Indicates the minimum required number of cores. For example: `"hostRequirements": { "gpu": {"cores": 2} }` |
29 | | `hostRequirements.gpu.memory` 🏷️ | string | A string indicating minimum memory requirements with a `tb`, `gb`, `mb`, or `kb` suffix. For example, `"hostRequirements": {"memory": "4gb"}` |
30 |
--------------------------------------------------------------------------------
/docs/specs/secrets-support.md:
--------------------------------------------------------------------------------
1 | # First Class Secrets Support
2 |
3 | This has now been implemented:
4 | * Discussion issue: https://github.com/devcontainers/spec/issues/219
5 | * CLI PR: https://github.com/devcontainers/cli/pull/493
6 |
7 | Below is the original proposal.
8 |
9 | ## What are secrets
10 | Secrets are variables that hold sensitive values and need to be handled securely at all times (API keys, passwords etc.). Users can change a secret's value at any time, and a conforming dev containers implementation should support dynamically changing secrets without having to rebuild the container.
11 |
12 | ## Goal
13 |
14 | Support secrets as a first class feature in dev containers implementations.
15 |
16 | This feature should consist of the ability to securely pass in the secrets, make them available for users to consume similar to remoteEnv and containerEnv, and be processed and handled securely at all times.
17 | This functionality will allow consumers to be able to use secrets more predictably and securely with their dev containers implementation.
18 |
19 | ## Motivation
20 |
21 | Today many consumers pass secret variables as `remoteEnv`, since there are no other mechanisms to pass them explicitly as secrets. The dev containers reference implementation do not (and need not) treat `remoteEnv` as secrets. Having explicit support for secrets will help eliminate security threats such as leaking secrets, as well as in improving customer confidence.
22 |
23 | ## Proposal
24 |
25 | A [supporting tool](https://containers.dev/supporting#tools) (e.g. the [dev container CLI reference implementation](https://github.com/devcontainers/cli)) should behave according to the following properties:
26 |
27 | 1. Ability to pass secrets to commands
28 | 2. Apply/use secrets similar to `remoteEnv`
29 | 3. Securely handle secrets
30 |
31 | ### Passing secrets in
32 | Secrets are not part of dev containers specification and we do not expect users to store secrets inside `devcontainer.json` file. A conforming implementation should provide a secure mechanism to pass secrets, such as a secrets file, Windows credential manager, Mac keychain, Azure keyvault etc. for example.
33 |
34 | #### **Example**
35 |
36 | Using a file to pass in the secrets can be one of the simple approaches to adopt. In this example the supporting tool can input secrets in a JSON file format.
37 |
38 | ```json
39 | {
40 | "API_KEY": "adsjhsd6dfwdjfwde7edwfwedfdjedwf7wedfwe",
41 | "NUGET_CONFIG": "\n \n \n",
42 | "PASSWORD": "Simple Passwords"
43 | }
44 | ```
45 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Security
4 |
5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
6 |
7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below.
8 |
9 | ## Reporting Security Issues
10 |
11 | **Please do not report security vulnerabilities through public GitHub issues.**
12 |
13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).
14 |
15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc).
16 |
17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
18 |
19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
20 |
21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
22 | * Full paths of source file(s) related to the manifestation of the issue
23 | * The location of the affected source code (tag/branch/commit or direct URL)
24 | * Any special configuration required to reproduce the issue
25 | * Step-by-step instructions to reproduce the issue
26 | * Proof-of-concept or exploit code (if possible)
27 | * Impact of the issue, including how an attacker might exploit the issue
28 |
29 | This information will help us triage your report more quickly.
30 |
31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs.
32 |
33 | ## Preferred Languages
34 |
35 | We prefer all communications to be in English.
36 |
37 | ## Policy
38 |
39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).
40 |
41 |
--------------------------------------------------------------------------------
/docs/specs/declarative-secrets.md:
--------------------------------------------------------------------------------
1 | # Declarative Secrets
2 |
3 | This has now been implemented:
4 | * Discussion issue: https://github.com/devcontainers/spec/issues/198, https://github.com/devcontainers/spec/issues/216
5 | * Schema PR: https://github.com/devcontainers/spec/pull/303
6 |
7 | Below is the original proposal.
8 |
9 | ## Motivation
10 |
11 | Various projects exist in the wild that require various secrets for them to run properly. Examples include:
12 |
13 | - https://github.com/lostintangent/codespaces-langchain
14 | - https://github.com/openai/openai-quickstart-python
15 | - https://github.com/openai/openai-quickstart-node
16 |
17 | Today these projects have to include specific instructions (e.g. in their README) telling users where to procure what they need to run and then how to set it up as a secret or add it as an environment variable. This currently acts as an impediment to adoption and promotion of dev containers for these projects.
18 |
19 | ## Goal
20 |
21 | Simplify using dev containers for these kinds of projects by supporting secrets as a first-class part of the dev container creation flow.
22 |
23 | ## Proposal
24 |
25 | Add an optional `secrets` property to the `devContainer.base.schema.json`. This will be used to declare the secrets needed within the dev container.
26 |
27 | Property | Type | Description
28 | --- | --- | ---
29 | `secrets` | `object` | The keys of this object are the names of the secrets that are in use. Keys should be [valid Linux environment variable names](https://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html).
30 |
31 | The `secrets` property contains a map of secret names and details about those secrets. These `secrets` are recommended to the user but are not _required_ for creation. All properties are optional.
32 |
33 | Property | Type | Description
34 | --- | --- | ---
35 | `description` | `string` | A brief description of the secret.
36 | `documentationUrl` | `string` | A URL pointing to where the user can obtain more information about the secret. For example where to find docs on provisioning an API key for a service.
37 |
38 | ## Example
39 |
40 | ```json
41 | {
42 | "image": "mcr.microsoft.com/devcontainers/base:bullseye",
43 | "secrets": {
44 | "CHAT_GPT_API_KEY": {
45 | "description": "I'm your super cool API key for ChatGPT.",
46 | "documentationUrl": "https://openai.com/api/"
47 | },
48 | "STABLE_DIFFUSION_API_KEY": {}
49 | }
50 | }
51 | ```
52 |
53 | This example would declare that this dev container wants the user to provide two secrets. The `CHAT_GPT_API_KEY` secret also provides some additional metadata that clients can use in displaying a user experience to provide that secret. The `STABLE_DIFFUSION_API_KEY` skips that metadata.
54 |
55 | Implementations _may_ inject these secrets into the container as environment variables.
56 |
57 | ## Possible Future Extensions
58 |
59 | These are **out of scope** for this proposal but the following has been discussed in relation:
60 |
61 | 1. Inclusion of a `type` field per secret. The default, which we are assuming and leveraging here, would be `"environmentVariable"`. This would include an option to specify `"reference"` and then reference the provided secrets in other parts of the dev container.
62 | 2. Prescriptions of _how_ the secrets are injected into the dev container.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Development Containers
2 |
3 | This repository is for the Development Container Specification. You can check out the spec on our website https://containers.dev/ too.
4 |
5 | A development container allows you to use a container as a full-featured development environment. It can be used to run an application, to separate tools, libraries, or runtimes needed for working with a codebase, and to aid in continuous integration and testing.
6 |
7 | The Development Container Specification seeks to find ways to enrich existing formats with common development specific settings, tools, and configuration while still providing a simplified, un-orchestrated single container option – so that they can be used as coding environments or for continuous integration and testing.
8 |
9 | 
10 |
11 | The first format in the specification, `devcontainer.json`, was born out of necessity. It is a structured JSON with Comments (jsonc) metadata format that tools can use to store any needed configuration required to develop inside of local or cloud-based containerized coding.
12 |
13 | We envision that this same structured data can be embedded in images and other formats – all while retaining a common object model for consistent processing. For example, some of this same metadata can be added to a `devcontainer.metadata` image label to tie settings directly to a container image.
14 |
15 | Beyond repeatable setup, these same development containers provide consistency to avoid environment specific problems across developers and centralized build and test automation services. You can use the [open-source CLI reference implementation](https://github.com/devcontainers/cli) either directly or integrated into product experiences to use the structured metadata to deliver these benefits. It currently supports integrating with Docker Compose and a simplified, un-orchestrated single container option – so that they can be used as coding environments or for continuous integration and testing.
16 |
17 | A GitHub Action and an Azure DevOps Task are available in [devcontainers/ci](https://github.com/devcontainers/ci) for running a repository's dev container in continuous integration (CI) builds. This allows you to reuse the same setup that you are using for local development to also build and test your code in CI.
18 |
19 | ### Spec content
20 |
21 | You may review the specification in the [docs/specs folder](https://github.com/devcontainers/spec/tree/main/docs/specs) of this repo.
22 |
23 | You may also review active proposals in the [proposals folder](https://github.com/devcontainers/spec/tree/main/proposals).
24 |
25 | Images used in this repo will be contained in the [images folder](/images). The icon for the [dev container GitHub org](https://github.com/devcontainers) is from the [Fluent icon library](https://github.com/microsoft/fluentui-system-icons/blob/master/assets/Cube/SVG/ic_fluent_cube_32_filled.svg).
26 |
27 | ## Contributing and Feedback
28 |
29 | If you are interested in contributing, please check out the [How to Contribute](contributing.md) document, open an issue, or [join our community Slack channel](https://aka.ms/dev-container-community).
30 |
31 | Please report issues in the following repositories:
32 |
33 | - Spec-maintained Features and Templates: [devcontainers/features](https://github.com/devcontainers/features), [devcontainers/templates](https://github.com/devcontainers/templates)
34 | - CLI reference implementation and non-spec related feature requests: [devcontainers/cli](https://github.com/devcontainers/cli)
35 | - GitHub Action and Azure DevOps Task: [devcontainers/ci](https://github.com/devcontainers/ci)
36 |
37 | # License
38 |
39 | License for this repository:
40 |
41 | Copyright © Microsoft Corporation All rights reserved.
42 | Creative Commons Attribution 4.0 License (International): https://creativecommons.org/licenses/by/4.0/legalcode
43 |
--------------------------------------------------------------------------------
/docs/specs/devcontainer-lockfile.md:
--------------------------------------------------------------------------------
1 | # Lockfiles
2 |
3 | This has now been implemented:
4 | * Discussion issue: https://github.com/devcontainers/spec/issues/236
5 | * CLI PR: https://github.com/devcontainers/cli/issues/564
6 |
7 | Below is the original proposal.
8 |
9 | ## Goal
10 |
11 | Introduce a lockfile that records the exact version, download information and checksums for each feature listed in the devcontainer.json.
12 |
13 | This will allow for:
14 | - Improved reproducibility of image builds (installing "latest" of a tool will still have different outcomes as the tool publishes new releases).
15 | - Improved cachability of image builds (image cache checksums will remain stable when the lockfile pins a feature to a particular version).
16 | - Improved security by detecting when a feature's release artifact changes after its checksum was first recorded in the lockfile ("trust on first use").
17 |
18 | ## Proposal
19 |
20 | (The following is inspired by NPM's `package-lock.json` and Yarn's `yarn.lock`.)
21 |
22 | Each feature is recorded with the identifier and version it is referred to in the devcontainer.json as its key and the following properties as its values:
23 | - `resolved`:
24 | - OCI feature: A qualified feature id with the sha256 hash (not the version number).
25 | - tarball feature: The `https:` URL used to download the feature.
26 | - `version`: The full version number.
27 | - `integrity`: `sha256:` followed by the hex encoded SHA-256 checksum of the download artifact.
28 | - `dependsOn`: An array of feature identifiers equal to the feature's `dependsOn` keys in its `devcontainer-feature.json` including any version or checksum suffix. If the array is empty, this property can be omitted. For every feature listed here, the lockfile will also have a feature record.
29 |
30 | The feature identifiers recorded as keys and in the `dependsOn` arrays are converted to lowercase to simplify lookups and comparisons. Note that the `devcontainer.json` and `devcontainer-features.json` may refer to the same features with different casing because these identifiers are not case-senstive.
31 |
32 | Local features and the deprecated GitHub releases features are not recorded in the lockfile.
33 |
34 | The lockfile is named `devcontainer-lock.json`, is located in the same folder as the `devcontainer.json` and contains a JSON object with a `"features"` property holding the above keys.
35 |
36 | Example:
37 |
38 | ```jsonc
39 | {
40 | "features": {
41 | "ghcr.io/devcontainers/features/node:1": {
42 | "version": "1.0.4",
43 | "resolved": "ghcr.io/devcontainers/features/node@sha256:567d704b3f4d3eca3acee51ded7c460a8395436d135d53d1175fb565daff42b8",
44 | "integrity": "sha256:567d704b3f4d3eca3acee51ded7c460a8395436d135d53d1175fb565daff42b8"
45 | },
46 | "https://mycustomdomain.com/devcontainer-feature-myfeature.tgz": {
47 | "version": "1.2.3",
48 | "resolved": "https://mycustomdomain.com/devcontainer-feature-myfeature.tgz",
49 | "integrity": "sha256:567d704b3f4d3eca3acee51ded7c460a8395436d135d53d1175fb565daff42b8"
50 | }
51 | }
52 | }
53 | ```
54 |
55 | Example with dependency:
56 |
57 | ```jsonc
58 | {
59 | "features": {
60 | "ghcr.io/codspace/dependson/a": {
61 | "version": "1.2.1",
62 | "resolved": "ghcr.io/codspace/dependson/a@sha256:932027ef71da186210e6ceb3294c3459caaf6b548d2b547d5d26be3fc4b2264a",
63 | "integrity": "sha256:932027ef71da186210e6ceb3294c3459caaf6b548d2b547d5d26be3fc4b2264a",
64 | "dependsOn": [
65 | "ghcr.io/codspace/dependson/e:2"
66 | ]
67 | },
68 | "ghcr.io/codspace/dependson/e:2": {
69 | "version": "2.3.4",
70 | "resolved": "ghcr.io/codspace/dependson/e@sha256:9f36f159c70f8bebff57f341904b030733adb17ef12a5d58d4b3d89b2a6c7d5a",
71 | "integrity": "sha256:9f36f159c70f8bebff57f341904b030733adb17ef12a5d58d4b3d89b2a6c7d5a"
72 | }
73 | }
74 | }
75 | ```
76 |
--------------------------------------------------------------------------------
/docs/specs/devcontainer-id-variable.md:
--------------------------------------------------------------------------------
1 | # Dev Container ID
2 |
3 | This has now been implemented:
4 | * Discussion issue: https://github.com/devcontainers/spec/issues/62
5 | * CLI PR: https://github.com/devcontainers/cli/pull/242
6 |
7 | Below is the original proposal.
8 |
9 | ## Goal
10 |
11 | Allow features to refer to an identifier that is unique to the dev container they are installed into and that is stable across rebuilds.
12 |
13 | E.g., the `docker-in-docker` feature needs a way to mount a volume per dev container.
14 |
15 | ## Proposal
16 |
17 | The identifier will be referred to as `${devcontainerId}` in the devcontainer.json and the feature metadata and that will be replaced with the dev container's id. It should only be used in parts of the configuration and metadata that is not used for building the image because that would otherwise prevent pre-building the image at a time when the dev container's id is not known yet. Excluding boolean, numbers and enum properties the properties supporting `${devcontainerId}` in the devcontainer.json are: `name`, `runArgs`, `initializeCommand`, `onCreateCommand`, `updateContentCommand`, `postCreateCommand`, `postStartCommand`, `postAttachCommand`, `workspaceFolder`, `workspaceMount`, `mounts`, `containerEnv`, `remoteEnv`, `containerUser`, `remoteUser`, `customizations`. Excluding boolean, numbers and enum properties the properties supporting `${devcontainerId}` in the feature metadata are: `entrypoint`, `mounts`, `customizations`.
18 |
19 | Implementations can choose how to compute this identifier. They must ensure that it is unique among other dev containers on the same Docker host and that it is stable across rebuilds of dev containers. The identifier must only contain alphanumeric characters. We describe a way to do this below.
20 |
21 | ### Label-based Implementation
22 |
23 | The following assumes that a dev container can be identified among other dev containers on the same Docker host by a set of labels on the container. Implementations may choose to follow this approach.
24 |
25 | The identifier is derived from the set of container labels uniquely identifying the dev container. It is up to the implementation to choose these labels. For example, if the dev container is based on a local folder, the label could be named `devcontainer.local_folder` and have the local folder's path as its value.
26 |
27 | For example, the [`ghcr.io/devcontainers/features/docker-in-docker` feature](https://github.com/devcontainers/features/blob/main/src/docker-in-docker/devcontainer-feature.json) could use the dev container id with:
28 |
29 | ```jsonc
30 | {
31 | "id": "docker-in-docker",
32 | "version": "1.0.4",
33 | // ...
34 | "mounts": [
35 | {
36 | "source": "dind-var-lib-docker-${devcontainerId}",
37 | "target": "/var/lib/docker",
38 | "type": "volume"
39 | }
40 | ]
41 | }
42 | ```
43 |
44 | ### Label-based Computation
45 |
46 | - Input the labels as a JSON object with the object's keys being the label names and the object's values being the labels' values.
47 | - To ensure implementations get to the same result, the object keys must be sorted and any optional whitespace outside of the keys and values must be removed.
48 | - Compute a SHA-256 hash from the UTF-8 encoded input string.
49 | - Use a base-32 encoded representation left-padded with '0' to 52 characters as the result.
50 |
51 | JavaScript implementation taking an object with the labels as argument and returning a string as the result:
52 | ```js
53 | const crypto = require('crypto');
54 |
55 | function uniqueIdForLabels(idLabels) {
56 | const stringInput = JSON.stringify(idLabels, Object.keys(idLabels).sort()); // sort properties
57 | const bufferInput = Buffer.from(stringInput, 'utf-8');
58 |
59 | const hash = crypto.createHash('sha256')
60 | .update(bufferInput)
61 | .digest();
62 |
63 | const uniqueId = BigInt(`0x${hash.toString('hex')}`)
64 | .toString(32)
65 | .padStart(52, '0');
66 | return uniqueId;
67 | }
68 | ```
69 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute to the Dev Container Specification
2 |
3 | We're excited for your contributions to the dev container specification! This document outlines how you can get involved.
4 |
5 | ## Contribution approaches
6 |
7 | If you'd like to contribute a change or addition to the spec, you may follow the guidance below:
8 | - Propose the change via an [issue](https://github.com/microsoft/dev-container-spec/issues) in this repository. Try to get early feedback before spending too much effort formalizing it.
9 | - More formally document the proposed change in terms of properties and their semantics. Look to format your proposal like our [devcontainer.json reference](https://aka.ms/devcontainer.json), which is a JSON with Comments (jsonc) format.
10 |
11 | Here is a sample:
12 |
13 | | Property | Type | Description |
14 | |----------|------|-------------|
15 | | `image` | string | **Required** when [using an image](/docs/remote/create-dev-container.md#using-an-image-or-dockerfile). The name of an image in a container registry ([DockerHub](https://hub.docker.com), [GitHub Container Registry](https://docs.github.com/packages/guides/about-github-container-registry), [Azure Container Registry](https://azure.microsoft.com/services/container-registry/)) that VS Code and other `devcontainer.json` supporting services / tools should use to create the dev container. |
16 |
17 | - PRs to the [schema](https://github.com/devcontainers/spec/blob/main/schemas/devContainer.base.schema.json), i.e code or shell scripts demonstrating approaches for implementation.
18 |
19 | Once there is discussion on your proposal, please also open and link a PR to update the [devcontainer.json reference doc](https://github.com/microsoft/vscode-docs/blob/main/docs/remote/devcontainerjson-reference.md). When your proposal is merged, the docs will be kept up-to-date with the latest spec.
20 |
21 | ### Contributing tool-specific support
22 |
23 | Tool-specific properties are contained in namespaces in the `"customizations"` property. For instance, VS Code specific properties are formatted as:
24 |
25 | ```bash
26 | // Configure tool-specific properties.
27 | "customizations": {
28 | // Configure properties specific to VS Code.
29 | "vscode": {
30 | // Set *default* container specific settings.json values on container create.
31 | "settings": {},
32 |
33 | // Additional VS Code specific properties...
34 | }
35 | },
36 | ```
37 |
38 | You may propose adding a new namespace for a specific tool, and any properties specific to that tool.
39 |
40 | ## Formatting Guidelines
41 |
42 | When contributing an official doc or referencing dev containers in your projects, please consider the following guidelines:
43 |
44 | - Refer to the spec as the "Development Container Specification"
45 | - All capital letters
46 | - Singular "Container" rather than plural "Containers"
47 | - The term "dev container" shouldn't be capitalized on its own
48 | - It should only be capitalized when referring to an official tool title, like the VS Code Dev Containers extension
49 | - Signify `devcontainer.json` is a file type through backticks
50 | - Features and Templates should always be capitalized
51 | - Refer to the CLI as the "Dev Container CLI" (note the caps)
52 | - Use bolding for emphasis sprinkled throughout sections, rather than try to use it to always bold certain terms
53 |
54 | ## Review process
55 |
56 | We use the following [labels](https://github.com/microsoft/dev-container-spec/labels):
57 |
58 | - `proposal`: Issues under discussion, still collecting feedback.
59 | - `finalization`: Proposals we intend to make part of the spec.
60 |
61 | [Milestones](https://github.com/microsoft/dev-container-spec/milestones) use a "month year" pattern (i.e. January 2022). If a finalized proposal is added to a milestone, it is intended to be merged during that milestone.
62 |
63 | ## Community Engagement
64 | There are several additional options to engage with the dev container community, such as asking questions, providing feedback, or engaging on how your team may use or contribute to dev containers:
65 | - [GitHub Discussions](https://github.com/devcontainers/spec/discussions): This is a great opportunity to connect with the community and maintainers of this project, without the requirement of contributing a change to the actual spec (which we see more in issues and PRs)
66 | - [Community Slack channel](https://aka.ms/dev-container-community): This is a great opportunity to connect with the community and maintainers
67 | - You can always check out the issues and PRs (and contribute new ones) across the repos in the [Dev Containers GitHub org](https://github.com/devcontainers) too!
68 |
--------------------------------------------------------------------------------
/docs/specs/features-contribute-lifecycle-scripts.md:
--------------------------------------------------------------------------------
1 | # Allow Dev Container Features to contribute lifecycle scripts
2 |
3 | This has now been implemented:
4 | * Discussion issue: https://github.com/devcontainers/spec/issues/60, https://github.com/devcontainers/spec/issues/181
5 | * CLI PR: https://github.com/devcontainers/cli/pull/390
6 |
7 | Below is the original proposal.
8 |
9 | ## Goal
10 |
11 | Allow Feature authors to provide lifecycle hooks for their Features during a dev container build.
12 |
13 | ## Proposal
14 |
15 | Introduce the following properties to [devcontainer-feature.json](https://containers.dev/implementors/features/#devcontainer-feature-json-properties), mirroring the behavior and syntax of the `devcontainer.json` lifecycle hooks.
16 |
17 | - "onCreateCommand"
18 | - "updateContentCommand"
19 | - "postCreateCommand"
20 | - "postStartCommand"
21 | - "postAttachCommand"
22 |
23 | Note that `initializeCommand` is omitted, pending further discussions around a secure design.
24 |
25 | As with all lifecycle hooks, commands are executed from the context (cwd) of the [project workspace folder](https://containers.dev/implementors/spec/#project-workspace-folder).
26 |
27 | > **Note:** To use any assets (a script, etc...) embedded within a Feature in a lifecycle hook property, it is the Feature author's reponsibility to copy that assert to somewhere on the container where it will persist (outside of `/tmp`.) Implementations are not required to persist Feature install scripts beyond the initial build.
28 |
29 | All other semantics match the existing [Lifecycle Scripts](https://containers.dev/implementors/json_reference/#lifecycle-scripts) and [lifecycle script parallel execution](https://containers.dev/implementors/spec/#parallel-exec) behavior exactly.
30 |
31 | ### Execution
32 |
33 | When a dev container is brought up, for each lifecycle hook, each Feature that contributes a command to a lifecycle hook shall have the command executed in sequence, following the same execution order as outlined in [Feature installation order](https://containers.dev/implementors/features/#installation-order), and always before any user-provided lifecycle commands.
34 |
35 | ## Examples
36 |
37 |
38 | ### Executing a script in the workspace folder
39 |
40 | The follow example illustrates contributing an `onCreateCommand` and `postCreateCommand` script to `featureA`. At each lifecycle hook during the build, the provided script will be executed in the [project workspace folder](https://containers.dev/implementors/spec/#project-workspace-folder), following the same semantics as [user-defined lifecycle hooks](https://containers.dev/implementors/json_reference/#lifecycle-scripts).
41 |
42 | ```jsonc
43 | {
44 | "id": "featureA",
45 | "version": "1.0.0",
46 | "onCreateCommand": "myOnCreate.sh && myOnCreate2.sh",
47 | "postCreateCommand": "myPostCreate.sh",
48 | "postAttachCommand": {
49 | "command01": "myPostAttach.sh arg01",
50 | "command02": "myPostAttach.sh arg02",
51 | },
52 | }
53 |
54 | ```
55 |
56 | ### Executing a script bundled with the Feature
57 |
58 | The following example illustrates executing a `postCreateCommand` script bundled with the Feature.
59 |
60 | ```jsonc
61 | {
62 | "id": "featureB",
63 | "version": "1.0.0",
64 | "postCreateCommand": "/usr/myDir/bundledScript.sh",
65 | "installsAfter": [
66 | "featureA"
67 | ]
68 | }
69 | ```
70 |
71 | The `install.sh` will need to move this script to `/usr/myDir`.
72 |
73 | ```
74 | #!/bin/sh
75 |
76 | cp ./bundleScript /usr/myDir
77 | ...
78 | ...
79 | ```
80 |
81 | Given this example, `featureB`'s file structure would look something like:
82 |
83 | ```
84 | .
85 | ├── src
86 | │ ├── featureB
87 | │ │ ├── bundledScript.sh
88 | │ │ ├── devcontainer-feature.json
89 | │ │ └── install.sh
90 | ```
91 |
92 | ### Installation
93 |
94 | Given the following `devcontainer.json` and the two example Features above.
95 |
96 | ```jsonc
97 | {
98 | "image": "ubuntu",
99 | "features": {
100 | "featureA": {},
101 | "featureB": {},
102 | },
103 | "postCreateCommand": "userPostCreate.sh",
104 | "postAttachCommand": {
105 | "server": "npm start",
106 | "db": ["mysql", "-u", "root", "-p", "my database"]
107 | },
108 | }
109 | ```
110 |
111 | The following timeline of events would occur.
112 |
113 | > Note that:
114 | >
115 | >1. Each bullet point below is _blocking_. No subsequent lifecycle commands shall proceed until the current command completes.
116 | > 2. If one of the lifecycle scripts fails, any subsequent scripts will not be executed. For instance, if a given `postCreate` command fails, the `postStart` hook and any following hooks will be skipped.
117 | >
118 |
119 | - `featureA`'s onCreateCommand
120 | - `featureA`'s postCreateCommand
121 | - `featureB`'s postCreateCommand
122 | - The user's postCreateCommand
123 | - `featureA`'s postAttachCommand **(parallel syntax, each command running in parallel)**
124 | - The user's postAttachCommand **(parallel syntax, each command running in parallel)**
125 |
126 | Suppose `featureB`'s postCreateCommand exited were to exit unsuccessfully (non-zero exit code). In this case, the `postAttachCommand` will never fire.
127 |
--------------------------------------------------------------------------------
/docs/specs/image-metadata.md:
--------------------------------------------------------------------------------
1 | # Image Metadata
2 |
3 | This has now been implemented:
4 | * Discussion issue: https://github.com/devcontainers/spec/issues/18
5 | * CLI PR: https://github.com/devcontainers/cli/pull/188
6 |
7 | Below is the original proposal.
8 |
9 | ## Goal
10 |
11 | Record dev container config and feature metadata in prebuilt images, such that, the image and the built-in features can be used with a devcontainer.json (image-, Dockerfile- or Docker Compose-based) that does not repeat the dev container config or feature metadata. Other tools should be able to record the same metadata without necessarily using features themselves.
12 |
13 | Current dev container config that can be recorded in the image: `mounts`, `onCreateCommand`, `updateContentCommand`, `postCreateCommand`, `postStartCommand`, `postAttachCommand`, `customizations`, `remoteUser`, `userEnvProbe`, `remoteEnv`, `containerEnv`, `overrideCommand`, `portsAttributes`, `otherPortsAttributes`, `forwardPorts`, `shutdownAction`, `updateRemoteUserUID` and `hostRequirements`.
14 |
15 | Current feature metadata relevant for using the feature when it is already part of the image: `mounts`, `init`, `privileged`, `capAdd`, `securityOpt`, `entrypoint` and `customizations`.
16 |
17 | We can add to these lists as we add more properties to the dev container configuration and the feature metadata.
18 |
19 | ## Proposal
20 |
21 | We propose to add metadata to the image with the following structure using one entry per feature and devcontainer.json (see table below for the full list):
22 |
23 | ```
24 | [
25 | {
26 | "id"?: string,
27 | "init"?: boolean,
28 | "privileged"?: boolean,
29 | "capAdd"?: string[],
30 | "securityOpt"?: string[],
31 | "entrypoint"?: string,
32 | "mounts"?: [],
33 | ...
34 | "customizations"?: {
35 | ...
36 | }
37 | },
38 | ...
39 | ]
40 | ```
41 |
42 | To simplify adding this metadata for other tools, we also support having a single top-level object with the same properties.
43 |
44 | The metadata is added to the image as a `devcontainer.metadata` label with a JSON string value representing the above array or single object.
45 |
46 | ## Merge Logic
47 |
48 | To apply the metadata together with a user's devcontainer.json at runtime the following merge logic by property is used. The table also notes which properties are currently supported coming from the devcontainer.json and which from the feature metadata, this will change over time as we add more properties.
49 |
50 | | Property | Type/Format | Merge Logic | devcontainer.json | Feature Metadata |
51 | | -------- | ----------- | ----------- | :---------------: | :--------------: |
52 | | `id` | E.g., `ghcr.io/devcontainers/features/node:1` | Not merged. | | x |
53 | | `init` | `boolean` | `true` if at least one is `true`, `false` otherwise. | x | x |
54 | | `privileged` | `boolean` | `true` if at least one is `true`, `false` otherwise. | x | x |
55 | | `capAdd` | `string[]` | Union of all `capAdd` arrays without duplicates. | x | x |
56 | | `securityOpt` | `string[]` | Union of all `securityOpt` arrays without duplicates. | x | x |
57 | | `entrypoint` | `string` | Collected list of all entrypoints. | | x |
58 | | `mounts` | `(string \| { type, src, dst })[]` | Collected list of all mountpoints. Conflicts: Last source wins. | x | x |
59 | | `onCreateCommand` | `string \| string[]` | Collected list of all onCreateCommands. | x | |
60 | | `updateContentCommand` | `string \| string[]` | Collected list of all updateContentCommands. | x | |
61 | | `postCreateCommand` | `string \| string[]` | Collected list of all postCreateCommands. | x | |
62 | | `postStartCommand` | `string \| string[]` | Collected list of all postStartCommands. | x | |
63 | | `postAttachCommand` | `string \| string[]` | Collected list of all postAttachCommands. | x | |
64 | | `waitFor` | enum | Last value wins. | x | |
65 | | `customizations` | Object of tool-specific customizations. | Merging is left to the tools. | x | x |
66 | | `containerUser` | `string` | Last value wins. | x | |
67 | | `remoteUser` | `string` | Last value wins. | x | |
68 | | `userEnvProbe` | `string` (enum) | Last value wins. | x | |
69 | | `remoteEnv` | Object of strings. | Per variable, last value wins. | x | |
70 | | `containerEnv` | Object of strings. | Per variable, last value wins. | x | |
71 | | `overrideCommand` | `boolean` | Last value wins. | x | |
72 | | `portsAttributes` | Map of ports to attributes. | Per port (not per port attribute), last value wins. | x | |
73 | | `otherPortsAttributes` | Port attributes. | Last value wins (not per port attribute). | x | |
74 | | `forwardPorts` | `(number \| string)[]` | Union of all ports without duplicates. Last one wins (when mapping changes). | x | |
75 | | `shutdownAction` | `string` (enum) | Last value wins. | x | |
76 | | `updateRemoteUserUID` | `boolean` | Last value wins. | x | |
77 | | `hostRequirements` | `cpus`, `memory`, `storage` | Max value wins. | x | |
78 |
79 | Variables in string values will be substituted at the time the value is applied. When the order matters, the devcontainer.json is considered last.
80 |
81 | ## Additional devcontainer.json Properties
82 |
83 | We are adding support for `mounts`, `containerEnv`, `containerUser`, `init`, `privileged`, `capAdd`, and `securityOpt` to the devcontainer.json (also with Docker Compose) the same way these properties are already supported in the feature metadata.
84 |
85 | ## Notes
86 |
87 | - Passing the label as a `LABEL` instruction in the Dockerfile:
88 | - The size limit on Dockerfiles is around 1.3MB. The line length is limited to 65k characters.
89 | - Using one line per feature should allow for making full use of these limits.
90 | - Passing the label as a command line argument:
91 | - There is no size limit documented for labels, but the daemon returns an error when the request header is >500kb.
92 | - The 500kb limit is shared, so we cannot use a second label in the same build to avoid it.
93 | - If/when this becomes an issue we could embed the metadata as a file in the image (e.g., with a label indicating it).
94 |
--------------------------------------------------------------------------------
/proposals/features-library.md:
--------------------------------------------------------------------------------
1 | # Sharing code between Dev Container Features
2 |
3 | ref: https://github.com/devcontainers/spec/issues/129
4 |
5 | # Motivation
6 |
7 | The current model for authoring Features encourages significant copy and paste between individual Features. Most of the Features published under the `devcontainers/features` namespace contain approximately 100 lines of [identical helper functions](https://github.com/devcontainers/features/blob/main/src/azure-cli/install.sh#L29-L164) before the first line of the script is executed. The process of maintaining these helper functions is tedious, error-prone, and leads to less readable Features.
8 |
9 | Additionally, [members of the Feature authoring community have expressed interest in sharing code between Features](https://github.com/orgs/devcontainers/discussions/10). Exploring the source code of self-published Features, it is evident that many authors have copy/pasted code from other Features. This is a sign that there is a need for a more efficient way to share code between Features.
10 |
11 | # Goal
12 |
13 | Provide a generic and simple way to share code between Features.
14 |
15 | # Proposals
16 |
17 | ## [Proposal A] Add: `include` property to `devcontainer-feature.json`
18 |
19 | Add an `include` property to the `devcontainer-feature.json`. This will be honored during the Feature [packaging stage](https://containers.dev/implementors/features-distribution/#packaging).
20 |
21 | The contents of the provided relative path is copied into the packaged Feature archive. Files or directories are recursively copied into the archive following the same directory structure as in the source code.
22 |
23 | The relative path must not escape the root directory - if this is attempted, the packaging stage will fail.
24 |
25 | Property | Type | Description
26 | --- | --- | ---
27 | `include` | `string[]` | An array of paths relative to the directory containing this `devcontainer-feature.json`. If the element is a folder, it is copied recursively. Must be prefixed with `.` or `..` to indicate the provided string is relative path.
28 |
29 | ### Example
30 |
31 | ```json
32 | {
33 | "name": "featureA",
34 | "version": "0.0.1",
35 | "include": [
36 | "../../utils/",
37 | "../../company-welcome-message.txt"
38 | ]
39 | }
40 | ```
41 |
42 | ## [Proposal B] Change: Structure of packaged Feature
43 |
44 | Change the file structure of the packaged Feature to mirror the directory structure of the source code. This will simplify the implementation of **Proposal A** and keep the relative paths consistent before and after packaging.
45 |
46 | When running the packaging step, each Feature is packaged separately under `./src/`. Other included directories will be copied into the root of the packaged artifact as indicated in **Proposal A**.
47 |
48 | An implementation should resolve the `devcontainer-feature.json` by checking for its presence in the following order of precedence:
49 |
50 | - `./src//devcontainer-feature.json`
51 | - `devcontainer-feature.json`
52 |
53 | From here forward, the proposed format will be used during the packaging step. For backwards compatibility, the existing format (with a `devcontainer-feature.json` at the top level) will be supported.
54 |
55 | ### Example
56 |
57 | Given this example repository containing a collection of Features.
58 |
59 | ```
60 | .
61 | ├── images
62 | │ ├── 1.png
63 | │ ├── 2.png
64 | ├── company-welcome-message.txt
65 | ├── utils
66 | | ├── common.sh
67 | │ └── helpers
68 | │ ├── a.sh
69 | │ └── ...
70 | |
71 | ├── src
72 | │ ├── featureA
73 | │ │ ├── devcontainer-feature.json
74 | │ │ ├── install.sh
75 | │ │ └── ...
76 | │ ├── featureB
77 | │ │ ├── devcontainer-feature.json
78 | │ │ └── install.sh
79 | │ │ └── ...
80 | | ├── ...
81 | │ │ ├── devcontainer-feature.json
82 | │ │ └── install.sh
83 | │ │ └── ...
84 | ├── ...
85 | ```
86 |
87 | When packaging Feature A (shown above in **Proposal A**), the resulting published archive will be structured as follows:
88 |
89 | ```
90 | .
91 | ├── company-welcome-message.txt
92 | ├── utils
93 | | ├── common.sh
94 | │ └── helpers
95 | │ ├── a.sh
96 | │ └── ...
97 | |
98 | ├── src
99 | │ ├── featureA
100 | │ │ ├── devcontainer-feature.json
101 | │ │ ├── install.sh
102 | │ │ └── ...
103 | ```
104 |
105 | The packaging step recursively copies the contents of the `utils` folder into the resulting archive. Additionally, the `company-welcome-message.txt` is packaged and distributed with the Feature.
106 |
107 | Note that the `images` folder is not included in the packaged Feature. This is because the `images` folder is not included in the `include` property of the `devcontainer-feature.json` file.
108 |
109 | #### Using an included script in a Feature
110 |
111 | The following example shows how a Feature could include a `utils` folder that contains a `common.sh` file.
112 |
113 | ```bash
114 | # utils/common.sh
115 | function common_function() {
116 | echo "Hello, '$1'"
117 | }
118 | ```
119 |
120 | ```bash
121 | # src/featureA/install.sh
122 |
123 | # Include common functions
124 | source "../../utils/common.sh"
125 |
126 | # Use common function
127 | common_function "devcontainers"
128 | ```
129 |
130 | -----
131 |
132 | # Possible Extensions
133 |
134 | This section provides hints to possible future work that could extend the proposal above. These are not part of the proposal, but are provided to help the community understand the potential future impact of this proposal.
135 |
136 | ## Distributing shared library Features
137 |
138 | In an effort to reduce the need for authors to copy/paste auxillary scripts between Feature namespaces, provide the ability to publish "library" Features. These Features would be published [following the Feature distribution spec](https://containers.dev/implementors/features-distribution/#distribution) under a new manifest type.
139 |
140 | A "library" can be included in a Feature's `include` property as an alternative to a relative path. By itself, a "library" Feature would not be installable.
141 |
142 | ```json
143 |
144 | {
145 | "name": "featureA",
146 | "version": "0.0.1",
147 | "include": [
148 | "ghcr.io/devcontainers/features/utils:0.0.1"
149 | ]
150 | }
151 |
--------------------------------------------------------------------------------
/docs/specs/features-legacyIds-deprecated-properties.md:
--------------------------------------------------------------------------------
1 | # Renaming and Deprecating Features
2 |
3 | This has now been implemented:
4 | * Discussion issue: https://github.com/devcontainers/spec/issues/146
5 | * CLI PR: https://github.com/devcontainers/cli/pull/346, https://github.com/devcontainers/cli/pull/335
6 |
7 | Below is the original proposal.
8 |
9 | ## Goal
10 |
11 | Adds support for renaming and deprecating a Feature.
12 |
13 | Adding two new properties to the [devcontainer-feature.json](../docs/specs/devcontainer-features.md#devcontainer-featurejson-properties) 👇
14 |
15 | ## 1. legacyIds
16 |
17 | | Property | Type | Description |
18 | | :--- | :--- | :--- |
19 | | `legacyIds` | array | Defaults to `[]`. Array of old IDs used to publish this Feature. The property is useful for renaming a currently published Feature within a single namespace. |
20 |
21 | ### Steps to rename a Feature
22 |
23 | 1. Update the Feature [source code](../docs/specs/features-distribution/#source-code) folder and the `id` property in the [devcontainer-feature.json properties](../docs/specs/devcontainer-features.md#devcontainer-featurejson-properties) to reflect the new `id`. Other properties (`name`, `documentationUrl`, etc.) can optionally be updated during this step.
24 | 2. Add or update the `legacyIds` property to the Feature, including the previously used `id`.
25 | 3. Bump the semantic version of the Feature.
26 | 4. Rerun the `devcontainer features publish` command, or equivalent tool that implements the [Features distribution specification](../docs/specs/features-distribution/#distribution).
27 |
28 | #### Example: Renaming a Feature
29 |
30 | Let's say we currently have a `docker-from-docker` Feature 👇
31 |
32 | Current `devcontainer-feature.json` :
33 |
34 | ```jsonc
35 | {
36 | "id": "docker-from-docker",
37 | "version": "2.0.1",
38 | "name": "Docker (Docker-from-Docker)",
39 | "documentationURL": "https://github.com/devcontainers/features/tree/main/src/docker-from-docker",
40 | ....
41 | }
42 | ```
43 |
44 | We'd want to rename this Feature to `docker-outside-of-docker`. The source code folder of the Feature will be updated to `docker-outside-of-docker` and the updated `devcontainer-feature.json` will look like 👇
45 |
46 | ```jsonc
47 | {
48 | "id": "docker-outside-of-docker",
49 | "version": "2.0.2",
50 | "name": "Docker (Docker-outside-of-Docker)",
51 | "documentationURL": "https://github.com/devcontainers/features/tree/main/src/docker-outside-of-docker",
52 | "legacyIds": [
53 | "docker-from-docker"
54 | ]
55 | ....
56 | }
57 | ```
58 |
59 | **Note** - The semantic version of the Feature defined by the `version` property should be **continued** and should not be restarted at `1.0.0`.
60 |
61 | #### Changes to the Features distribution specification
62 |
63 | - [Tools implementing the Dev Container Features Distribution Specification](../docs/specs/features-distribution/#distribution) (e.g. [Dev Container CLI](https://github.com/devcontainers/cli) and [Dev Container Publish GitHub Action](https://github.com/marketplace/actions/dev-container-publish)) will dual publish the old `id`s (defined by the `legacyIds` property), and the new `id`.
64 | - The [putManifestWithTags](https://github.com/devcontainers/cli/blob/main/src/spec-configuration/containerCollectionsOCIPush.ts#L172) will be modified. The same `tgz` file for the `id` will be pushed to the `id`s mentioned by the `legacyIds` property for all the [tags](https://github.com/devcontainers/cli/blob/main/src/spec-configuration/containerCollectionsOCIPush.ts#L175).
65 |
66 | #### Supporting backwards compatibility for [`installsAfter`](../docs/specs/devcontainer-features.md#2-the-installsafter-feature-property) property
67 |
68 | - Currently the `installsAfter` property is defined as an array consisting of the Feature `id` that should be installed before the given Feature.
69 | - The Feature to be renamed could be already defined by other Feature authors in their `installsAfter` property. Renaming the `id` could change the installation order for them if the `installsAfter` property is not updated with the new `id`. In order to avoid this unexpected behavior and to support back compat, the CLI tooling will be updated to also look at the `legacyIds` property along with the `id` for determining the installation order.
70 |
71 | #### Supporting tools
72 |
73 | This property can be used by the supporting tools to indicate Feature rename in few ways
74 | - [Dev Container Publish GitHub Action](https://github.com/devcontainers/action) which auto-generates the README files can add a note with a list of old `id`s which were used to publish this Feature.
75 | - In the scenarios where Dev Configs are referencing the old `id`s, the VS Code extension hints could be enhanced to deliver this warning that the `id` was renamed to a new one.
76 | - The [containers.dev/features](https://containers.dev/features) website, and [supporting tools](https://containers.dev/supporting) like [VS Code Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) and [GitHub Codespaces](https://github.com/features/codespaces) wouldn't list the old `id`, instead list the new `id` of Features.
77 |
78 | ## 2. deprecated
79 |
80 | | Property | Type | Description |
81 | | :--- | :--- | :--- |
82 | | `deprecated` | boolean | Defaults to `false`. Indicates that the Feature is deprecated, and will not receive any further updates/support. This property is intended to be used by the supporting tools to highlight Feature deprecation. |
83 |
84 | - If this property is set to `true`, then it indicates that the Feature is deprecated and won't be receiving any further updates/support. The OCI artifacts would exist, hence, the current dev configs will continue to work.
85 | - In case of security or other ciritical updates, the Feature author could still publish a new version of the deprecated Feature by bumping the semantic version of the Feature.
86 | - This property could be used by the supporting tools to indicate Feature deprecation in few ways -
87 | - [Dev Container Publish GitHub Action](https://github.com/devcontainers/action) which auto-generates the README files can be updated to add a top-level header mentioning it's deprecation.
88 | - The [containers.dev/features](https://containers.dev/features) website, and [supporting tools](https://containers.dev/supporting) like [VS Code Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) and [GitHub Codespaces](https://github.com/features/codespaces) wouldn't list the deprecated Features.
89 | - VS Code extension hints could be enhanced to deliver this information about it's deprecation.
90 |
--------------------------------------------------------------------------------
/docs/specs/devcontainer-templates-distribution.md:
--------------------------------------------------------------------------------
1 | # Distribution and Discovery of Dev Container Templates
2 |
3 | **TL;DR Check out the [quick start repository](https://github.com/devcontainers/template-starter) to get started on distributing your own Dev Container Templates.**
4 |
5 | This specification defines a pattern where community members and organizations can author and self-publish [Dev Container Templates](./devcontainer-templates.md).
6 |
7 | Goals include:
8 |
9 | - For Template authors, create a "self-service" way to publish a Template, either publicly or privately, that is not centrally controlled.
10 | - Provide the ability to standardize publishing such that supporting tools may implement their own mechanism to aid Template discoverability as they see fit.
11 |
12 | ## Source code
13 |
14 | A Template's source code is stored in a git repository.
15 |
16 | 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. `/`).
17 |
18 | > **Note:** Templates and [Features](./devcontainer-features.md) should be placed in different git repositories.
19 |
20 | Source code for a set of Templates follows the example file structure below:
21 |
22 | ```
23 | .
24 | ├── README.md
25 | ├── src
26 | │ ├── dotnet
27 | │ │ ├── devcontainer-template.json
28 | │ │ ├── .devcontainer
29 | │ │ ├── devcontainer.json
30 | │ │ └── ...
31 | │ │ ├── ...
32 | | ├
33 | │ ├── docker-from-docker
34 | │ │ ├── devcontainer-template.json
35 | │ │ ├── .devcontainer
36 | │ │ ├── devcontainer.json
37 | │ │ ├── Dockerfile
38 | │ │ └── ...
39 | │ │ ├── ...
40 | | ├
41 | │ ├── go-postgres
42 | │ │ ├── devcontainer-template.json
43 | │ │ ├── .devcontainer
44 | │ │ ├── devcontainer.json
45 | │ │ ├── docker-compose.yml
46 | │ │ ├── Dockerfile
47 | │ │ └── ...
48 | │ │ ├── ...
49 | ```
50 |
51 | ...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/devcontainer.json` that the supporting tools will drop into an existing project or folder.
52 |
53 | 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 be included in the [packaging step](#packaging).
54 |
55 | ## Versioning
56 |
57 | 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.
58 |
59 | 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.
60 |
61 | ## Packaging
62 |
63 | Templates are distributed as tarballs. The tarball contains the entire contents of the Template sub-directory, including the `devcontainer-template.json`, `.devcontainer/devcontainer.json`, and any other files in the directory.
64 |
65 | The tarball is named `devcontainer-template-.tgz`, where `` is the Templates's `id` field.
66 |
67 | A reference implementation for packaging and distributing Templates is provided as a GitHub Action (https://github.com/devcontainers/action).
68 |
69 | ### devcontainer-collection.json
70 |
71 | The `devcontainer-collection.json` is an auto-generated metadata file.
72 |
73 | | Property | Type | Description |
74 | | :--- | :--- | :--- |
75 | | `sourceInformation` | object | Metadata from the implementing packaging tool. |
76 | | `templates` | array | The list of Templates that are contained in this collection.|
77 |
78 | Each Template's `devcontainer-template.json` metadata file is appended into the `templates` top-level array.
79 |
80 | ## Distribution
81 |
82 | There are several supported ways to distribute Templates. Distribution is handled by the implementing packaging tool such as the **[Dev Container CLI](https://github.com/devcontainers/cli)** or **[Dev Container Publish GitHub Action](https://github.com/marketplace/actions/dev-container-publish)**.
83 |
84 | 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).
85 |
86 | ### OCI Registry
87 |
88 | An OCI registry that implements the [OCI Artifact Distribution Specification](https://github.com/opencontainers/distribution-spec) serves as the primary distribution mechanism for Templates.
89 |
90 | Each packaged Template is pushed to the registry following the naming convention `//[:version]`, where version is the major, minor, and patch version of the Template, according to the semver specification.
91 |
92 | > **Note:** `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 `namespace`; however, one pattern is to set `namespace` equal to source repository's `/`.
93 |
94 | A custom media type `application/vnd.devcontainers` and `application/vnd.devcontainers.layer.v1+tar` are used as demonstrated below.
95 |
96 | For example, the `go` Template in the `devcontainers/templates` namespace at version `1.2.3` would be pushed to the ghcr.io OCI registry.
97 |
98 | > **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.
99 |
100 | ```bash
101 | # ghcr.io/devcontainers/templates/go:1
102 | REGISTRY=ghcr.io
103 | NAMESPACE=devcontainers/templates
104 | TEMPLATE=go
105 |
106 | ARTIFACT_PATH=devcontainer-template-go.tgz
107 |
108 | for VERSION in 1 1.2 1.2.3 latest
109 | do
110 | oras push ${REGISTRY}/${NAMESPACE}/${TEMPLATE}:${VERSION} \
111 | --config /dev/null:application/vnd.devcontainers \
112 | ./${ARTIFACT_PATH}:application/vnd.devcontainers.layer.v1+tar
113 | done
114 |
115 | ```
116 |
117 | The "namespace" is the globally identifiable name for the collection of Templates (e.g. `owner/repo` for the source code's git repository).
118 |
119 | 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`.
120 |
121 | ```bash
122 | # ghcr.io/devcontainers/templates
123 | REGISTRY=ghcr.io
124 | NAMESPACE=devcontainers/templates
125 |
126 | oras push ${REGISTRY}/${NAMESPACE}:latest \
127 | --config /dev/null:application/vnd.devcontainers \
128 | ./devcontainer-collection.json:application/vnd.devcontainers.collection.layer.v1+json
129 | ```
130 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Aa][Rr][Mm]/
27 | [Aa][Rr][Mm]64/
28 | bld/
29 | [Bb]in/
30 | [Oo]bj/
31 | [Ll]og/
32 | [Ll]ogs/
33 |
34 | # Visual Studio 2015/2017 cache/options directory
35 | .vs/
36 | # Uncomment if you have tasks that create the project's static files in wwwroot
37 | #wwwroot/
38 |
39 | # Visual Studio 2017 auto generated files
40 | Generated\ Files/
41 |
42 | # MSTest test Results
43 | [Tt]est[Rr]esult*/
44 | [Bb]uild[Ll]og.*
45 |
46 | # NUnit
47 | *.VisualState.xml
48 | TestResult.xml
49 | nunit-*.xml
50 |
51 | # Build Results of an ATL Project
52 | [Dd]ebugPS/
53 | [Rr]eleasePS/
54 | dlldata.c
55 |
56 | # Benchmark Results
57 | BenchmarkDotNet.Artifacts/
58 |
59 | # .NET Core
60 | project.lock.json
61 | project.fragment.lock.json
62 | artifacts/
63 |
64 | # StyleCop
65 | StyleCopReport.xml
66 |
67 | # Files built by Visual Studio
68 | *_i.c
69 | *_p.c
70 | *_h.h
71 | *.ilk
72 | *.meta
73 | *.obj
74 | *.iobj
75 | *.pch
76 | *.pdb
77 | *.ipdb
78 | *.pgc
79 | *.pgd
80 | *.rsp
81 | *.sbr
82 | *.tlb
83 | *.tli
84 | *.tlh
85 | *.tmp
86 | *.tmp_proj
87 | *_wpftmp.csproj
88 | *.log
89 | *.vspscc
90 | *.vssscc
91 | .builds
92 | *.pidb
93 | *.svclog
94 | *.scc
95 |
96 | # Chutzpah Test files
97 | _Chutzpah*
98 |
99 | # Visual C++ cache files
100 | ipch/
101 | *.aps
102 | *.ncb
103 | *.opendb
104 | *.opensdf
105 | *.sdf
106 | *.cachefile
107 | *.VC.db
108 | *.VC.VC.opendb
109 |
110 | # Visual Studio profiler
111 | *.psess
112 | *.vsp
113 | *.vspx
114 | *.sap
115 |
116 | # Visual Studio Trace Files
117 | *.e2e
118 |
119 | # TFS 2012 Local Workspace
120 | $tf/
121 |
122 | # Guidance Automation Toolkit
123 | *.gpState
124 |
125 | # ReSharper is a .NET coding add-in
126 | _ReSharper*/
127 | *.[Rr]e[Ss]harper
128 | *.DotSettings.user
129 |
130 | # TeamCity is a build add-in
131 | _TeamCity*
132 |
133 | # DotCover is a Code Coverage Tool
134 | *.dotCover
135 |
136 | # AxoCover is a Code Coverage Tool
137 | .axoCover/*
138 | !.axoCover/settings.json
139 |
140 | # Visual Studio code coverage results
141 | *.coverage
142 | *.coveragexml
143 |
144 | # NCrunch
145 | _NCrunch_*
146 | .*crunch*.local.xml
147 | nCrunchTemp_*
148 |
149 | # MightyMoose
150 | *.mm.*
151 | AutoTest.Net/
152 |
153 | # Web workbench (sass)
154 | .sass-cache/
155 |
156 | # Installshield output folder
157 | [Ee]xpress/
158 |
159 | # DocProject is a documentation generator add-in
160 | DocProject/buildhelp/
161 | DocProject/Help/*.HxT
162 | DocProject/Help/*.HxC
163 | DocProject/Help/*.hhc
164 | DocProject/Help/*.hhk
165 | DocProject/Help/*.hhp
166 | DocProject/Help/Html2
167 | DocProject/Help/html
168 |
169 | # Click-Once directory
170 | publish/
171 |
172 | # Publish Web Output
173 | *.[Pp]ublish.xml
174 | *.azurePubxml
175 | # Note: Comment the next line if you want to checkin your web deploy settings,
176 | # but database connection strings (with potential passwords) will be unencrypted
177 | *.pubxml
178 | *.publishproj
179 |
180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
181 | # checkin your Azure Web App publish settings, but sensitive information contained
182 | # in these scripts will be unencrypted
183 | PublishScripts/
184 |
185 | # NuGet Packages
186 | *.nupkg
187 | # NuGet Symbol Packages
188 | *.snupkg
189 | # The packages folder can be ignored because of Package Restore
190 | **/[Pp]ackages/*
191 | # except build/, which is used as an MSBuild target.
192 | !**/[Pp]ackages/build/
193 | # Uncomment if necessary however generally it will be regenerated when needed
194 | #!**/[Pp]ackages/repositories.config
195 | # NuGet v3's project.json files produces more ignorable files
196 | *.nuget.props
197 | *.nuget.targets
198 |
199 | # Microsoft Azure Build Output
200 | csx/
201 | *.build.csdef
202 |
203 | # Microsoft Azure Emulator
204 | ecf/
205 | rcf/
206 |
207 | # Windows Store app package directories and files
208 | AppPackages/
209 | BundleArtifacts/
210 | Package.StoreAssociation.xml
211 | _pkginfo.txt
212 | *.appx
213 | *.appxbundle
214 | *.appxupload
215 |
216 | # Visual Studio cache files
217 | # files ending in .cache can be ignored
218 | *.[Cc]ache
219 | # but keep track of directories ending in .cache
220 | !?*.[Cc]ache/
221 |
222 | # Others
223 | ClientBin/
224 | ~$*
225 | *~
226 | *.dbmdl
227 | *.dbproj.schemaview
228 | *.jfm
229 | *.pfx
230 | *.publishsettings
231 | orleans.codegen.cs
232 |
233 | # Including strong name files can present a security risk
234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
235 | #*.snk
236 |
237 | # Since there are multiple workflows, uncomment next line to ignore bower_components
238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
239 | #bower_components/
240 |
241 | # RIA/Silverlight projects
242 | Generated_Code/
243 |
244 | # Backup & report files from converting an old project file
245 | # to a newer Visual Studio version. Backup files are not needed,
246 | # because we have git ;-)
247 | _UpgradeReport_Files/
248 | Backup*/
249 | UpgradeLog*.XML
250 | UpgradeLog*.htm
251 | ServiceFabricBackup/
252 | *.rptproj.bak
253 |
254 | # SQL Server files
255 | *.mdf
256 | *.ldf
257 | *.ndf
258 |
259 | # Business Intelligence projects
260 | *.rdl.data
261 | *.bim.layout
262 | *.bim_*.settings
263 | *.rptproj.rsuser
264 | *- [Bb]ackup.rdl
265 | *- [Bb]ackup ([0-9]).rdl
266 | *- [Bb]ackup ([0-9][0-9]).rdl
267 |
268 | # Microsoft Fakes
269 | FakesAssemblies/
270 |
271 | # GhostDoc plugin setting file
272 | *.GhostDoc.xml
273 |
274 | # Node.js Tools for Visual Studio
275 | .ntvs_analysis.dat
276 | node_modules/
277 |
278 | # Visual Studio 6 build log
279 | *.plg
280 |
281 | # Visual Studio 6 workspace options file
282 | *.opt
283 |
284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
285 | *.vbw
286 |
287 | # Visual Studio LightSwitch build output
288 | **/*.HTMLClient/GeneratedArtifacts
289 | **/*.DesktopClient/GeneratedArtifacts
290 | **/*.DesktopClient/ModelManifest.xml
291 | **/*.Server/GeneratedArtifacts
292 | **/*.Server/ModelManifest.xml
293 | _Pvt_Extensions
294 |
295 | # Paket dependency manager
296 | .paket/paket.exe
297 | paket-files/
298 |
299 | # FAKE - F# Make
300 | .fake/
301 |
302 | # CodeRush personal settings
303 | .cr/personal
304 |
305 | # Python Tools for Visual Studio (PTVS)
306 | __pycache__/
307 | *.pyc
308 |
309 | # Cake - Uncomment if you are using it
310 | # tools/**
311 | # !tools/packages.config
312 |
313 | # Tabs Studio
314 | *.tss
315 |
316 | # Telerik's JustMock configuration file
317 | *.jmconfig
318 |
319 | # BizTalk build output
320 | *.btp.cs
321 | *.btm.cs
322 | *.odx.cs
323 | *.xsd.cs
324 |
325 | # OpenCover UI analysis results
326 | OpenCover/
327 |
328 | # Azure Stream Analytics local run output
329 | ASALocalRun/
330 |
331 | # MSBuild Binary and Structured Log
332 | *.binlog
333 |
334 | # NVidia Nsight GPU debugger configuration file
335 | *.nvuser
336 |
337 | # MFractors (Xamarin productivity tool) working folder
338 | .mfractor/
339 |
340 | # Local History for Visual Studio
341 | .localhistory/
342 |
343 | # BeatPulse healthcheck temp database
344 | healthchecksdb
345 |
346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
347 | MigrationBackup/
348 |
349 | # Ionide (cross platform F# VS Code tools) working folder
350 | .ionide/
351 |
--------------------------------------------------------------------------------
/docs/specs/supporting-tools.md:
--------------------------------------------------------------------------------
1 | # Supporting Tools and Services
2 |
3 | **Note: For the latest set of supporting tools, please check out https://containers.dev/supporting.**
4 |
5 | This page outlines tools and services that currently support the Development Container Specification, including the `devcontainer.json` format. A `devcontainer.json` file in your project tells tools and services that support the dev container spec how to access (or create) a dev container with a well-defined tool and runtime stack.
6 |
7 | While most [dev container properties](devcontainerjson-reference.md) apply to any supporting tool or service, a few are specific to certain tools, which are outlined below.
8 |
9 | ## Editors
10 |
11 | ### Visual Studio Code
12 |
13 | Visual Studio Code specific properties go under `vscode` inside `customizations`.
14 |
15 |
16 | ```jsonc
17 | "customizations": {
18 | // Configure properties specific to VS Code.
19 | "vscode": {
20 | // Set *default* container specific settings.json values on container create.
21 | "settings": {},
22 | "extensions": [],
23 | }
24 | }
25 | ```
26 |
27 |
28 | | Property | Type | Description |
29 | |----------|------|-------------|
30 | | `extensions` | array | An array of extension IDs that specify the extensions that should be installed inside the container when it is created. Defaults to `[]`. |
31 | | `settings` | object | Adds default `settings.json` values into a container/machine specific settings file. Defaults to `{}`. |
32 |
33 | Please note that the VS Code [Dev Containers](#visual-studio-code-remote---containers) extension and [GitHub Codespaces](#github-codespaces) support the VS Code properties.
34 |
35 | ### Visual Studio
36 |
37 | Visual Studio added dev container support in Visual Studio 2022 17.4 for C++ projects using CMake Presets. It is part of the Linux and embedded development with C++ workload, so make sure it is selected in your VS installation. Visual Studio manages the lifecycle of dev containers it uses as you work, but it treats them as remote targets in a similar way to other Linux or WSL targets.
38 |
39 | You may learn more in the [announcement blog post](https://devblogs.microsoft.com/cppblog/dev-containers-for-c-in-visual-studio/).
40 |
41 | ## Tools
42 |
43 | ### Dev Container CLI
44 |
45 | A dev container command line interface (CLI) that implements this specification. It is in development in the [devcontainers/cli](https://github.com/devcontainers/cli) repo.
46 |
47 | ### VS Code extension CLI
48 |
49 | VS Code has a [CLI](https://code.visualstudio.com/docs/remote/devcontainer-cli) which may be installed within the Dev Containers extension or through the command line.
50 |
51 | ### Cachix devenv
52 |
53 | Cachix's [devenv](https://devenv.sh/) supports automatically generating a `.devcontainer.json` file so you can use it with any Dev Container Spec supporting tool. See [devenv documentation](https://devenv.sh/integrations/codespaces-devcontainer/) for detais.
54 |
55 | ### Jetpack.io Devbox
56 |
57 | [Jetpack.io's VS Code extension](https://marketplace.visualstudio.com/items?itemName=jetpack-io.devbox) supports a **Generate Dev Container files** command so you can use Jetpack.io from Dev Container Spec supporting tools.
58 |
59 | ### Visual Studio Code Dev Containers
60 |
61 | The [**Visual Studio Code Dev Containers** extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) lets you use a container as a full-featured development environment. It allows you to open any folder inside (or mounted into) a container and take advantage of Visual Studio Code's full feature set. There is more information in the Dev Containers [documentation](https://code.visualstudio.com/docs/remote/containers).
62 |
63 | > **Tip:** If you've already built a container and connected to it, be sure to run **Dev Containers: Rebuild Container** from the Command Palette (`kbstyle(F1)`) to pick up any changes you make.
64 |
65 | #### Product specific properties
66 |
67 | Dev Containers implements the [VS Code](#visual-studio-code) specific properties.
68 |
69 | #### Product specific limitations
70 |
71 | Some properties may also have certain limitations in the Dev Containers extension.
72 |
73 | | Property or variable | Type | Description |
74 | |----------|------|-------------|
75 | | `workspaceMount` | string | Not yet supported when using Clone Repository in Container Volume. |
76 | | `workspaceFolder` | string | Not yet supported when using Clone Repository in Container Volume. |
77 | | `${localWorkspaceFolder}` | Any | Not yet supported when using Clone Repository in Container Volume. |
78 | | `${localWorkspaceFolderBasename}` | Any | Not yet supported when using Clone Repository in Container Volume. |
79 |
80 | ### GitHub Codespaces
81 |
82 | A [codespace](https://docs.github.com/en/codespaces/overview) is a development environment that's hosted in the cloud. Codespaces run on a variety of VM-based compute options hosted by GitHub.com, which you can configure from 2 core machines up to 32 core machines. You can connect to your codespaces from the browser or locally using Visual Studio Code.
83 |
84 | > **Tip:** If you've already built a codespace and connected to it, be sure to run **Codespaces: Rebuild Container** from the Command Palette (`kbstyle(F1)`) to pick up any changes you make.
85 |
86 | > **Tip:** Codespaces implements an auto `workspaceFolder` mount in **Docker Compose** scenarios.
87 |
88 | #### Product specific properties
89 | GitHub Codespaces works with a growing number of tools and, where applicable, their `devcontainer.json` properties. For example, connecting the codespaces web editor or VS Code enables the use of [VS Code properties](#visual-studio-code).
90 |
91 | If your codespaces project needs additional permissions for other repositories, you can configure this through the `repositories` and `permissions` properties. You may learn more about this in the [Codespaces documentation](https://docs.github.com/en/codespaces/managing-your-codespaces/managing-repository-access-for-your-codespaces). As with other tools, Codespaces specific properties are placed within a `codespaces` namespace inside the `customizations` property.
92 |
93 | ```jsonc
94 | "customizations": {
95 | // Configure properties specific to Codespaces.
96 | "codespaces": {
97 | "repositories": {
98 | "my_org/my_repo": {
99 | "permissions": {
100 | "issues": "write"
101 | }
102 | }
103 | }
104 | }
105 | }
106 | ```
107 |
108 | You can customize which files are initially opened when the codespace is created:
109 | ```jsonc
110 | "customizations": {
111 | // Configure properties specific to Codespaces.
112 | "codespaces": {
113 | "openFiles": [
114 | "README"
115 | "src/index.js"
116 | ]
117 | }
118 | }
119 | ```
120 |
121 | The paths are relative to the root of the repository. They will be opened in order, with the first file activated.
122 |
123 | Codespaces will automatically perform some default setup when the `devcontainer.json` does not specify a `postCreateCommand`. This can be disabled with the `disableAutomaticConfiguration` setting:
124 |
125 | ```jsonc
126 | "customizations": {
127 | // Configure properties specific to Codespaces.
128 | "codespaces": {
129 | "disableAutomaticConfiguration": true
130 | }
131 | }
132 | ```
133 |
134 | Note that currently codespaces reads these properties from `devcontainer.json`, not image metadata.
135 |
136 | #### Product specific limitations
137 |
138 | Some properties may apply differently to codespaces.
139 |
140 | | Property or variable | Type | Description |
141 | |----------|---------|----------------------|
142 | | `mounts` | array | Codespaces ignores "bind" mounts with the exception of the Docker socket. Volume mounts are still allowed.|
143 | | `forwardPorts` | array | Codespaces does not yet support the `"host:port"` variation of this property. |
144 | | `portsAttributes` | object | Codespaces does not yet support the `"host:port"` variation of this property.|
145 | | `shutdownAction` | enum | Does not apply to Codespaces. |
146 | | `${localEnv:VARIABLE_NAME}` | Any | For Codespaces, the host is in the cloud rather than your local machine.|
147 | | `customizations.codespaces` | object | Codespaces reads this property from devcontainer.json, not image metadata. |
148 | | `hostRequirements` | object | Codespaces reads this property from devcontainer.json, not image metadata. |
149 |
--------------------------------------------------------------------------------
/docs/specs/devcontainer-templates.md:
--------------------------------------------------------------------------------
1 | # Dev Container Templates reference
2 |
3 | **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.
4 |
5 | The configuration is placed in [`.devcontainer/devcontainer.json`](/docs/specs/devcontainer-reference.md#devcontainerjson) which can also reference other files within the Template. A Template can also provide additional source files (e.g. boilerplate code or a [lifecycle script](https://containers.dev/implementors/json_reference/#lifecycle-scripts).
6 |
7 | Template metadata is captured by a `devcontainer-template.json` file in the root folder of the Template.
8 |
9 | ## Folder structure
10 |
11 | A single Template is a folder with at least a `devcontainer-template.json` and [`.devcontainer/devcontainer.json`](/docs/specs/devcontainer-reference.md#devcontainerjson). Additional files are permitted and are packaged along side the required files.
12 |
13 | ```
14 | +-- template
15 | | +-- devcontainer-template.json
16 | | +-- .devcontainer
17 | | +-- devcontainer.json
18 | | +-- (other files)
19 | | +-- (other files)
20 | ```
21 |
22 | ## devcontainer-template.json properties
23 |
24 | 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).
25 |
26 | The properties of the file are as follows:
27 |
28 | | Property | Type | Description |
29 | | :--- | :--- | :--- |
30 | | `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. |
31 | | `version` | string | The semantic version of the Template. |
32 | | `name` | string | Name of the Template. |
33 | | `description` | string | Description of the Template. |
34 | | `documentationURL` | string | Url that points to the documentation of the Template. |
35 | | `licenseURL` | string | Url that points to the license of the Template. |
36 | | [`options`](#the-options-property) | object | A map of options that the supporting tools should use to populate different configuration options for the Template. |
37 | | `platforms` | array | Languages and platforms supported by the Template. |
38 | | `publisher` | string | Name of the publisher/maintainer of the Template. |
39 | | `keywords` | array | List of strings relevant to a user that would search for this Template. |
40 | | [`optionalPaths`](#the-optionalpaths-property) | array | An array of files or directories that tooling may consider "optional" when applying a Template. Directories are indicated with a trailing `/*` (e.g. `.github/*`).
41 |
42 | ### The `options` property
43 |
44 | 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 all the files (within the sub-directory of the Template). This replacement would happen before dropping the `.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:
45 |
46 | ```json
47 | {
48 | "options": {
49 | "optionId": {
50 | "type": "string",
51 | "description": "Description of the option",
52 | "proposals": ["value1", "value2"],
53 | "default": "value1"
54 | }
55 | }
56 | }
57 | ```
58 |
59 | | Property | Type | Description |
60 | | :--- | :--- | :--- |
61 | | `optionId` | string | ID of the option used by the supporting tools to replace the selected value in the files within the sub-directory of the Template. |
62 | | `optionId.type` | string | Type of the option. Valid types are currently: `boolean`, `string` |
63 | | `optionId.description` | string | Description for the option. |
64 | | `optionId.proposals` | array | A list of suggested string values. Free-form values **are** allowed. Omit when using `optionId.enum`. |
65 | | `optionId.enum` | array | A strict list of allowed string values. Free-form values are **not** allowed. Omit when using `optionId.proposals`. |
66 | | `optionId.default` | string | Default value for the option. |
67 |
68 | > **Note:** The `options` must be unique for every `devcontainer-template.json`
69 |
70 | ### The `optionalPaths` property
71 |
72 | Before applying a Template, tooling must inspect the `optionalPaths` property of a Template and prompt the user on whether each file or folder should be included in the resulting output workspace folder. A path is relative to the root of the Template source directory.
73 |
74 | - For a single file, provide the full relative path (without any leading or trailing path delimiters).
75 | - For a directory, provide the full relative path with a trailing slash and asterisk (`/*`) appended to the path. The directory and its children will be recursively ignored.
76 |
77 | Examples are shown below:
78 |
79 | ```jsonc
80 | {
81 | "id": "cpp",
82 | "version": "3.0.0",
83 | "name": "C++",
84 | "description": "Develop C++ applications",
85 | "optionalPaths": [
86 | "GETTING-STARTED.md", // Single file
87 | "example-project-1/MyProject.csproj", // Single file in nested directory
88 | ".github/*" // Entire recursive contents of directory
89 | ]
90 | }
91 | ```
92 |
93 | ### Referencing a Template
94 |
95 | The `id` format (`//[:]`) dictates how a [supporting tool](https://containers.dev/supporting) will locate and download a given Template from an OCI registry. For example:
96 |
97 | - `ghcr.io/user/repo/go`
98 | - `ghcr.io/user/repo/go:1`
99 | - `ghcr.io/user/repo/go:latest`
100 |
101 | 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/).
102 |
103 | ## Versioning
104 |
105 | 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.
106 |
107 | 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.
108 |
109 | ## Release
110 |
111 | _For information on distributing Templates, see [devcontainer-templates-distribution.md](./devcontainer-templates-distribution.md)._
112 |
113 | ### Option Resolution
114 |
115 | 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 files (within the sub-directory of the Template).
116 |
117 | ### Option resolution example
118 |
119 | Consider a `java` Template with the following folder structure:
120 |
121 | ```
122 | +-- java
123 | | +-- devcontainer-template.json
124 | | +-- .devcontainer
125 | | +-- devcontainer.json
126 | ```
127 |
128 | Suppose the `java` Template has the following `options` parameters declared in the `devcontainer-template.json` file:
129 |
130 | ```json
131 | // ...
132 | "options": {
133 | "imageVariant": {
134 | "type": "string",
135 | "description": "Specify version of java.",
136 | "proposals": [
137 | "17-bullseye",
138 | "17-buster",
139 | "11-bullseye",
140 | "11-buster",
141 | "17",
142 | "11"
143 | ],
144 | "default": "17-bullseye"
145 | },
146 | "nodeVersion": {
147 | "type": "string",
148 | "proposals": [
149 | "latest",
150 | "16",
151 | "14",
152 | "10",
153 | "none"
154 | ],
155 | "default": "latest",
156 | "description": "Specify version of node, or 'none' to skip node installation."
157 | },
158 | "installMaven": {
159 | "type": "boolean",
160 | "description": "Install Maven, a management tool for Java.",
161 | "default": "false"
162 | },
163 | }
164 | ```
165 |
166 | ...and it has the following `.devcontainer/devcontainer.json` file:
167 |
168 | ```json
169 | {
170 | "name": "Java",
171 | "image": "mcr.microsoft.com/devcontainers/java:0-${templateOption:imageVariant}",
172 | "features": {
173 | "ghcr.io/devcontainers/features/node:1": {
174 | "version": "${templateOption:nodeVersion}",
175 | "installMaven": "${templateOption:installMaven}"
176 | }
177 | },
178 | // ...
179 | }
180 | ```
181 |
182 | 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"`.
183 |
184 | The supporting tool could then use a string replacer for all the files within the sub-directory of the Template. In this example, `.devcontainer/devcontainer.json` needs to be modified and hence, the inputs can provided to it as follows:
185 |
186 | ```
187 | {
188 | imageVariant:"17-bullseye",
189 | nodeVersion: "latest",
190 | installMaven: "false"
191 | }
192 | ```
193 |
194 | The modified `.devcontainer/devcontainer.json` will be as follows:
195 |
196 | ```json
197 | {
198 | "name": "Go",
199 | "image": "mcr.microsoft.com/devcontainers/go:0-17-bullseye",
200 | "features": {
201 | "ghcr.io/devcontainers/features/node:1": {
202 | "version": "latest",
203 | "installMaven": "false"
204 | }
205 | },
206 | ...
207 | }
208 | ```
209 |
210 | The modified `.devcontainer/devcontainer.json` would be dropped into any existing folder as a starting point for containerizing your project.
211 |
--------------------------------------------------------------------------------
/docs/specs/devcontainer-features-distribution.md:
--------------------------------------------------------------------------------
1 | # Distribution and Discovery of Dev Container Features
2 |
3 | **TL;DR Check out the [quick start repository](https://github.com/devcontainers/feature-template) to get started on distributing your own Dev Container Features.**
4 |
5 | This specification defines a pattern where community members and organizations can author and self-publish [Dev Container Features](./devcontainer-features.md).
6 |
7 | Goals include:
8 |
9 | - For Feature authors, create a "self-service" way to publish a Feature, either publicly or privately, that is not centrally controlled.
10 | - For users, provide the ability to validate the integrity of fetched Feature assets.
11 | - For users, provide the ability to pin to a particular version (absolute, or semantic version) of a Feature to allow for consistent, repeatable environments.
12 | - Provide the ability to standardize publishing such that [supporting tools](../docs/specs/supporting-tools.md) may implement their own mechanism to aid Feature discoverability as they see fit.
13 |
14 | > **Tip:** This section covers details on the Features specification. If you are looking for summarized information on creating your own Features, see the [template](https://github.com/devcontainers/feature-template) and [core Features](https://github.com/devcontainers/features) repositories.
15 |
16 | ## Source code
17 |
18 | Features source code is stored in a git repository.
19 |
20 | For ease of authorship and maintenance, [1..n] features can share a single git repository. This set of features is referred to as a "collection," and will share the same [`devcontainer-collection.json`](#devcontainer-collection.json) file and "namespace" (eg. `/`).
21 |
22 | Source code for the set follows the example file structure below:
23 |
24 | ```
25 | .
26 | ├── README.md
27 | ├── src
28 | │ ├── dotnet
29 | │ │ ├── devcontainer-feature.json
30 | │ │ ├── install.sh
31 | │ │ └── ...
32 | | ├
33 | │ ├── go
34 | │ │ ├── devcontainer-feature.json
35 | │ │ └── install.sh
36 | | ├── ...
37 | │ │ ├── devcontainer-feature.json
38 | │ │ └── install.sh
39 | ├── test
40 | │ ├── dotnet
41 | │ │ ├── test.sh
42 | │ │ └── ...
43 | │ └── go
44 | │ | └── test.sh
45 | | ├── ...
46 | │ │ └── test.sh
47 | ├── ...
48 | ```
49 |
50 | ...where `src` is a directory containing a sub-folder with the name of the Feature (e.g. `src/dotnet` or `src/go`) with at least a file named `devcontainer-feature.json` that contains the Feature metadata, and an `install.sh` script that implementing tools will use as the entrypoint to install the Feature.
51 |
52 | Each sub-directory should be named such that it matches the `id` field of the `devcontainer-feature.json`. Other files can also be included in the Feature'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 Feature's sub-directory (e.g. outside of `src/dotnet`) will not be included in the [packaging step](#packaging).
53 |
54 | Optionally, a mirrored `test` directory can be included with an accompanying `test.sh` script. Implementing tools may use this to run tests against the given Feature.
55 |
56 | ## Versioning
57 |
58 | Each Feature is individually [versioned according to the semver specification](https://semver.org/). The `version` property in the respective `devcontainer-feature.json` file is parsed to determine if the Feature should be republished.
59 |
60 | Tooling that handles publishing Features will not republish features if that exact version has already been published; however, tooling must republish major and minor versions in accordance with the semver specification.
61 |
62 | ## Packaging
63 |
64 | Features are distributed as tarballs. The tarball contains the entire contents of the Feature sub-directory, including the `devcontainer-feature.json`, `install.sh`, and any other files in the directory.
65 |
66 | The tarball is named `devcontainer-feature-.tgz`, where `` is the Feature's `id` field.
67 |
68 | A reference implementation for packaging and distributing Features is provided as a GitHub Action (https://github.com/devcontainers/action).
69 |
70 | ### devcontainer-collection.json
71 |
72 | The `devcontainer-collection.json` is an auto-generated metadata file.
73 |
74 | | Property | Type | Description |
75 | | :--- | :--- | :--- |
76 | | `sourceInformation` | object | Metadata from the implementing packaging tool. |
77 | | `features` | array | The list of features that are contained in this collection.|
78 |
79 | Each Features's `devcontainer-feature.json` metadata file is appended into the `features` top-level array.
80 |
81 | ## Distribution
82 |
83 | There are several supported ways to distribute Features. Distribution is handled by the implementing packaging tool such as the [Dev Container CLI](https://github.com/devcontainers/cli) or [Dev Container Publish GitHub Action](https://github.com/marketplace/actions/dev-container-publish). See the [quick start repository](https://github.com/devcontainers/feature-template) for a full working example.
84 |
85 | > Feature identifiers should be provided lowercase by an author. If not, an implementing packaging tool should normalize the identifier into lowercase for any internal representation.
86 |
87 | A user references a distributed Feature in a `devcontainer.json` as defined in ['referencing a feature'](./devcontainer-features.md#Referencing-a-feature).
88 |
89 | ### OCI Registry
90 |
91 | An OCI registry that implements the [OCI Artifact Distribution Specification](https://github.com/opencontainers/distribution-spec) serves as the primary distribution mechanism for Features.
92 |
93 | Each packaged feature is pushed to the registry following the naming convention `//[:version]`, where version is the major, minor, and patch version of the Feature, according to the semver specification.
94 |
95 | > **Note:** `namespace` is a unique identifier for the collection of Features. There are no strict rules for `namespace`; however, one pattern is to set `namespace` equal to source repository's `/`. The `namespace` should be lowercase, following [the regex provided in the OCI specification](https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pulling-manifests).
96 |
97 | A custom media type `application/vnd.devcontainers` and `application/vnd.devcontainers.layer.v1+tar` are used as demonstrated below.
98 |
99 | For example, the `go` Feature in the `devcontainers/features` namespace at version `1.2.3` would be pushed to the ghcr.io OCI registry.
100 |
101 | > **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.
102 |
103 | ```bash
104 | # ghcr.io/devcontainers/features/go:1
105 | REGISTRY=ghcr.io
106 | NAMESPACE=devcontainers/features
107 | FEATURE=go
108 |
109 | ARTIFACT_PATH=devcontainer-feature-go.tgz
110 |
111 | for VERSION in 1 1.2 1.2.3 latest
112 | do
113 | oras push ${REGISTRY}/${NAMESPACE}/${FEATURE}:${VERSION} \
114 | --manifest-config /dev/null:application/vnd.devcontainers \
115 | ./${ARTIFACT_PATH}:application/vnd.devcontainers.layer.v1+tar
116 | done
117 |
118 | ```
119 |
120 | The "namespace" is the globally identifiable name for the collection of Features (e.g. `owner/repo` for the source code's git repository).
121 |
122 | The auto-generated `devcontainer-collection.json` is pushed to the registry with the same `namespace` as above and no accompanying `feature` name. The collection file is always tagged as `latest`.
123 |
124 | ```bash
125 | # ghcr.io/devcontainers/features
126 | REGISTRY=ghcr.io
127 | NAMESPACE=devcontainers/features
128 |
129 | oras push ${REGISTRY}/${NAMESPACE}:latest \
130 | --manifest-config /dev/null:application/vnd.devcontainers \
131 | ./devcontainer-collection.json:application/vnd.devcontainers.collection.layer.v1+json
132 | ```
133 |
134 | Additionally, an [annotation](https://github.com/opencontainers/image-spec/blob/main/annotations.md) named `dev.containers.metadata` should be populated on the manifest when published by an implementing tool. This annotation is the escaped JSON object of the entire `devcontainer-feature.json` as it appears during the [packaging stage](https://containers.dev/implementors/features-distribution/#packaging).
135 |
136 | An example manifest with the `dev.containers.metadata` annotation:
137 |
138 | ```json
139 | {
140 | "schemaVersion": 2,
141 | "mediaType": "application/vnd.oci.image.manifest.v1+json",
142 | "config": {
143 | "mediaType": "application/vnd.devcontainers",
144 | "digest": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
145 | "size": 0
146 | },
147 | "layers": [
148 | {
149 | "mediaType": "application/vnd.devcontainers.layer.v1+tar",
150 | "digest": "sha256:738af5504b253dc6de51d2cb1556cdb7ce70ab18b2f32b0c2f12650ed6d2e4bc",
151 | "size": 3584,
152 | "annotations": {
153 | "org.opencontainers.image.title": "devcontainer-feature-myFeature.tgz"
154 | }
155 | }
156 | ],
157 | "annotations": {
158 | "dev.containers.metadata": "{\"name\": \"My Feature\",\"id\": \"myFeature\",\"version\": \"1.0.0\",\"dependsOn\": {\"ghcr.io/myotherFeature:1\": {\"flag\": true},\"features.azurecr.io/aThirdFeature:1\": {},\"features.azurecr.io/aFourthFeature:1.2.3\": {}}}"
159 | }
160 | }
161 | ```
162 |
163 | ### Directly referencing a tarball
164 |
165 | A Feature can be referenced directly in a user's [`devcontainer.json`](/docs/specs/devcontainer-reference.md#devcontainerjson) file by HTTPS URI that points to the tarball from the [package step](#packaging).
166 |
167 | The `.tgz` archive file must be named `devcontainer-feature-.tgz`.
168 |
169 | ### Locally referenced Features
170 |
171 | Instead of publishing a Feature to an OCI registry, a Feature's source code may be referenced from a local folder. Locally referencing a Feature may be useful when first authoring a Feature.
172 |
173 | A local Feature is referenced in the devcontainer's `feature` object **relative to the folder containing the project's `devcontainer.json`**.
174 |
175 | Additional constraints exists when including local Features in a project:
176 |
177 | * The project must have a `.devcontainer/` folder at the root of the [**project workspace folder**](/docs/specs/devcontainer-reference.md#project-workspace-folder).
178 |
179 | * A local Feature's source code **must** be contained within a sub-folder of the `.devcontainer/ folder`.
180 |
181 | * The sub-folder name **must** match the Feature's `id` field.
182 |
183 | * A local Feature may **not** be referenced by absolute path.
184 |
185 | * The local Feature's sub-folder **must** contain at least a `devcontainer-feature.json` file and `install.sh` entrypoint script, mirroring the [previously outlined file structure](#Source-code).
186 |
187 | The relative path is provided using unix-style path syntax (e.g. `./myFeature`) regardless of the host operating system.
188 |
189 | An example project is illustrated below:
190 |
191 | ```
192 | .
193 | ├── .devcontainer/
194 | │ ├── localFeatureA/
195 | │ │ ├── devcontainer-feature.json
196 | │ │ ├── install.sh
197 | │ │ └── ...
198 | │ ├── localFeatureB/
199 | │ │ ├── devcontainer-feature.json
200 | │ │ ├── install.sh
201 | │ │ └── ...
202 | │ ├── devcontainer.json
203 | ```
204 |
205 | ##### devcontainer.json
206 | ```jsonc
207 | {
208 | // ...
209 | "features": {
210 | "./localFeatureA": {},
211 | "./localFeatureB": {}
212 | }
213 |
214 | }
215 | ```
216 |
--------------------------------------------------------------------------------
/schemas/devContainerFeature.schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json-schema.org/draft-07/schema#",
3 | "title": "Development Container Feature Metadata",
4 | "description": "Development Container Features Metadata (devcontainer-feature.json). See https://containers.dev/implementors/features/ for more information.",
5 | "definitions": {
6 | "Feature": {
7 | "additionalProperties": false,
8 | "properties": {
9 | "capAdd": {
10 | "description": "Passes docker capabilities to include when creating the dev container.",
11 | "examples": [
12 | "SYS_PTRACE"
13 | ],
14 | "items": {
15 | "type": "string"
16 | },
17 | "type": "array"
18 | },
19 | "containerEnv": {
20 | "description": "Container environment variables.",
21 | "additionalProperties": {
22 | "type": "string"
23 | },
24 | "type": "object"
25 | },
26 | "customizations": {
27 | "description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations.",
28 | "additionalProperties": true,
29 | "type": "object"
30 | },
31 | "description": {
32 | "description": "Description of the Feature. For the best appearance in an implementing tool, refrain from including markdown or HTML in the description.",
33 | "type": "string"
34 | },
35 | "documentationURL": {
36 | "description": "URL to documentation for the Feature.",
37 | "type": "string"
38 | },
39 | "keywords": {
40 | "description": "List of strings relevant to a user that would search for this definition/Feature.",
41 | "items": {
42 | "type": "string"
43 | },
44 | "type": "array"
45 | },
46 | "entrypoint": {
47 | "description": "Entrypoint script that should fire at container start up.",
48 | "type": "string"
49 | },
50 | "id": {
51 | "description": "ID of the Feature. The id should be unique in the context of the repository/published package where the feature exists and must match the name of the directory where the devcontainer-feature.json resides.",
52 | "type": "string"
53 | },
54 | "init": {
55 | "description": "Adds the tiny init process to the container (--init) when the Feature is used.",
56 | "type": "boolean"
57 | },
58 | "installsAfter": {
59 | "description": "Array of ID's of Features that should execute before this one. Allows control for feature authors on soft dependencies between different Features.",
60 | "items": {
61 | "type": "string"
62 | },
63 | "type": "array"
64 | },
65 | "dependsOn": {
66 | "description": "An object of Feature dependencies that must be satisified before this Feature is installed. Elements follow the same semantics of the features object in devcontainer.json",
67 | "additionalProperties": true,
68 | "type": "object"
69 | },
70 | "licenseURL": {
71 | "description": "URL to the license for the Feature.",
72 | "type": "string"
73 | },
74 | "mounts": {
75 | "description": "Mounts a volume or bind mount into the container.",
76 | "items": {
77 | "$ref": "#/definitions/Mount"
78 | },
79 | "type": "array"
80 | },
81 | "name": {
82 | "description": "Display name of the Feature.",
83 | "type": "string"
84 | },
85 | "options": {
86 | "description": "Possible user-configurable options for this Feature. The selected options will be passed as environment variables when installing the Feature into the container.",
87 | "additionalProperties": {
88 | "$ref": "#/definitions/FeatureOption"
89 | },
90 | "type": "object"
91 | },
92 | "privileged": {
93 | "description": "Sets privileged mode (--privileged) for the container.",
94 | "type": "boolean"
95 | },
96 | "securityOpt": {
97 | "description": "Sets container security options to include when creating the container.",
98 | "items": {
99 | "type": "string"
100 | },
101 | "type": "array"
102 | },
103 | "version": {
104 | "description": "The version of the Feature. Follows the semanatic versioning (semver) specification.",
105 | "type": "string"
106 | },
107 | "legacyIds": {
108 | "description": "Array of old IDs used to publish this Feature. The property is useful for renaming a currently published Feature within a single namespace.",
109 | "items": {
110 | "type": "string"
111 | },
112 | "type": "array"
113 | },
114 | "deprecated": {
115 | "description": "Indicates that the Feature is deprecated, and will not receive any further updates/support. This property is intended to be used by the supporting tools for highlighting Feature deprecation.",
116 | "type": "boolean"
117 | },
118 | "onCreateCommand": {
119 | "type": [
120 | "string",
121 | "array",
122 | "object"
123 | ],
124 | "description": "A command to run when creating the container. This command is run after \"initializeCommand\" and before \"updateContentCommand\". If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell. If this is an object, each provided command will be run in parallel.",
125 | "items": {
126 | "type": "string"
127 | },
128 | "additionalProperties": {
129 | "type": [
130 | "string",
131 | "array"
132 | ],
133 | "items": {
134 | "type": "string"
135 | }
136 | }
137 | },
138 | "updateContentCommand": {
139 | "type": [
140 | "string",
141 | "array",
142 | "object"
143 | ],
144 | "description": "A command to run when creating the container and rerun when the workspace content was updated while creating the container. This command is run after \"onCreateCommand\" and before \"postCreateCommand\". If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell. If this is an object, each provided command will be run in parallel.",
145 | "items": {
146 | "type": "string"
147 | },
148 | "additionalProperties": {
149 | "type": [
150 | "string",
151 | "array"
152 | ],
153 | "items": {
154 | "type": "string"
155 | }
156 | }
157 | },
158 | "postCreateCommand": {
159 | "type": [
160 | "string",
161 | "array",
162 | "object"
163 | ],
164 | "description": "A command to run after creating the container. This command is run after \"updateContentCommand\" and before \"postStartCommand\". If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell. If this is an object, each provided command will be run in parallel.",
165 | "items": {
166 | "type": "string"
167 | },
168 | "additionalProperties": {
169 | "type": [
170 | "string",
171 | "array"
172 | ],
173 | "items": {
174 | "type": "string"
175 | }
176 | }
177 | },
178 | "postStartCommand": {
179 | "type": [
180 | "string",
181 | "array",
182 | "object"
183 | ],
184 | "description": "A command to run after starting the container. This command is run after \"postCreateCommand\" and before \"postAttachCommand\". If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell. If this is an object, each provided command will be run in parallel.",
185 | "items": {
186 | "type": "string"
187 | },
188 | "additionalProperties": {
189 | "type": [
190 | "string",
191 | "array"
192 | ],
193 | "items": {
194 | "type": "string"
195 | }
196 | }
197 | },
198 | "postAttachCommand": {
199 | "type": [
200 | "string",
201 | "array",
202 | "object"
203 | ],
204 | "description": "A command to run when attaching to the container. This command is run after \"postStartCommand\". If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell. If this is an object, each provided command will be run in parallel.",
205 | "items": {
206 | "type": "string"
207 | },
208 | "additionalProperties": {
209 | "type": [
210 | "string",
211 | "array"
212 | ],
213 | "items": {
214 | "type": "string"
215 | }
216 | }
217 | }
218 | },
219 | "required": [
220 | "id",
221 | "version"
222 | ],
223 | "type": "object"
224 | },
225 | "FeatureOption": {
226 | "anyOf": [
227 | {
228 | "description": "Option value is represented with a boolean value.",
229 | "additionalProperties": false,
230 | "properties": {
231 | "default": {
232 | "description": "Default value if the user omits this option from their configuration.",
233 | "type": "boolean"
234 | },
235 | "description": {
236 | "description": "A description of the option displayed to the user by a supporting tool.",
237 | "type": "string"
238 | },
239 | "type": {
240 | "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.",
241 | "const": "boolean",
242 | "type": "string"
243 | }
244 | },
245 | "required": [
246 | "type",
247 | "default"
248 | ],
249 | "type": "object"
250 | },
251 | {
252 | "additionalProperties": false,
253 | "properties": {
254 | "default": {
255 | "description": "Default value if the user omits this option from their configuration.",
256 | "type": "string"
257 | },
258 | "description": {
259 | "description": "A description of the option displayed to the user by a supporting tool.",
260 | "type": "string"
261 | },
262 | "enum": {
263 | "description": "Allowed values for this option. Unlike 'proposals', the user cannot provide a custom value not included in the 'enum' array.",
264 | "items": {
265 | "type": "string"
266 | },
267 | "type": "array"
268 | },
269 | "type": {
270 | "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.",
271 | "const": "string",
272 | "type": "string"
273 | }
274 | },
275 | "required": [
276 | "type",
277 | "enum",
278 | "default"
279 | ],
280 | "type": "object"
281 | },
282 | {
283 | "additionalProperties": false,
284 | "properties": {
285 | "default": {
286 | "description": "Default value if the user omits this option from their configuration.",
287 | "type": "string"
288 | },
289 | "description": {
290 | "description": "A description of the option displayed to the user by a supporting tool.",
291 | "type": "string"
292 | },
293 | "proposals": {
294 | "description": "Suggested values for this option. Unlike 'enum', the 'proposals' attribute indicates the installation script can handle arbitrary values provided by the user.",
295 | "items": {
296 | "type": "string"
297 | },
298 | "type": "array"
299 | },
300 | "type": {
301 | "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.",
302 | "const": "string",
303 | "type": "string"
304 | }
305 | },
306 | "required": [
307 | "type",
308 | "default"
309 | ],
310 | "type": "object"
311 | }
312 | ]
313 | },
314 | "Mount": {
315 | "description": "Mounts a volume or bind mount into the container.",
316 | "additionalProperties": false,
317 | "properties": {
318 | "source": {
319 | "description": "Mount source.",
320 | "type": "string"
321 | },
322 | "target": {
323 | "description": "Mount target.",
324 | "type": "string"
325 | },
326 | "type": {
327 | "description": "Type of mount. Can be 'bind' or 'volume'.",
328 | "enum": [
329 | "bind",
330 | "volume"
331 | ],
332 | "type": "string"
333 | }
334 | },
335 | "required": [
336 | "type",
337 | "target"
338 | ],
339 | "type": "object"
340 | }
341 | },
342 | "oneOf": [
343 | {
344 | "type": "object",
345 | "$ref": "#/definitions/Feature"
346 | }
347 | ]
348 | }
349 |
--------------------------------------------------------------------------------
/docs/specs/feature-dependencies.md:
--------------------------------------------------------------------------------
1 | # Feature Dependencies
2 |
3 | This has now been implemented:
4 | * Discussion issue: https://github.com/devcontainers/spec/issues/109
5 | * CLI PR: https://github.com/devcontainers/cli/commit/236b85162a945a1af9e72fcbe02eb5c7c864b31d
6 |
7 | Below is the original proposal.
8 |
9 | ## Motivation
10 |
11 | We've seen significant interest in the ability to "reuse" or "extend" a given Feature with one or more additional Features. Often in software a given tool depends on another (or several) tool(s)/framework(s). As the dev container Feature ecosystem has grown, there has been a growing need to reduce redundant code in Features by first installing a set of dependent Features.
12 |
13 | ## Goal
14 |
15 | The solution shall provide a way to publish a Feature that "depends" on >= 1 other published Features. Dependent Features will be installed by the orchestrating tool following this specification.
16 |
17 | The solution outlined shall not only execute the installation scripts, but also merge the additional development container config, as outlined in the documented [merging logic](https://containers.dev/implementors/spec/#merge-logic).
18 |
19 | The solution should provide guidelines for Feature authorship, and prefer solutions that enable simple authoring of Features, as well as easy consumption by end-users.
20 |
21 | A non-goal is to require the use or implementation of a full-blown dependency management system (such as `npm` or `apt`). The solution should allow Features to operate as "self-contained, shareable units of installation code and development container configuration"[(1)](https://containers.dev/implementors/features/).
22 |
23 | ## Definitions
24 |
25 | - **User-defined Feature**. Features defined explicitly by the user in the `features` object of `devcontainer.json`.
26 | - **Published Feature**. Features published to an OCI registry or a direct HTTPS link to a Feature tgz.
27 |
28 | ## Existing Solutions
29 |
30 | See https://github.com/devcontainers/spec/pull/208/files#diff-a29ffaac693437b6fbf001066b97896f7aef4d6d37dc65b8b98b22a5e19f6c7aR26 for existing solutions and alternative approaches.
31 |
32 | ## Proposed Specification
33 |
34 | ### (A) Add `dependsOn` property
35 |
36 | A new property `dependsOn` can be optionally added to the `devcontainer-feature.json`. This property mirrors the `features` object in `devcontainer.json`. Adding Feature(s) to this property tells the orchestrating tool to install the Feature(s) (with the associated options, if provided) before installing the Feature that declares the dependency.
37 |
38 | > This property is similar to the existing `installsAfter` property, with the key distinctions that `installsAfter` (1) is **not** recursive, (2) indicates a soft dependency to influence installation order **if and only if a given Feature is already set to be installed via a user-defined Feature or transitively through a user-defined Feature**, and (3) Features indicated by `installsAfter` can not provide options, nor are they able to be pinned to a specific version tag or digest.
39 |
40 | The installation order is subject to the algorithm set forth in this document. Where there is ambiguity, it is up to the orchestrating tool to decide the order of installation. Implementing tools should provide a consistent installation order in instances of ambiguity (i.e. sort alphanumerically by identifier).
41 |
42 | | Property | Type | Description |
43 | |----------|------|-------------|
44 | | `dependsOn` | `object` | The ID and options of the Feature dependency. Pinning to version tags or digests are honored. When published, the ID must refer to either a Feature (1) published to an OCI registry, (2) a Feature Tgz URI, or (3) a Feature in the local file tree (**). Otherwise, IDs follow the same semantics of the `features` object in `devcontainer.json`. (a.k.a "Hard Dependency") |
45 |
46 |
47 | (**) Deprecated Feature identifiers (i.e. GitHub Release) are not supported and the presence of this property may be considered a fatal error or ignored. For [local Features (i.e. during development)](https://containers.dev/implementors/features-distribution/#addendum-locally-referenced), you may also depend on other local Features by providing a relative path to the Feature, relative to folder containing the active `devcontainer.json`. This behavior of Features within this property again mirror the `features` object in `devcontainer.json`.
48 |
49 | An example `devcontainer-feature.json` file with a dependency on four other published Features:
50 |
51 | ```json
52 | {
53 | "name": "My Feature",
54 | "id": "myFeature",
55 | "version": "1.0.0",
56 | "dependsOn": {
57 | "ghcr.io/second:1": {
58 | "flag": true
59 | },
60 | "features.azurecr.io/third:1": {},
61 | "features.azurecr.io/fourth:1.2.3": {},
62 | "features.azurecr.io/fifth@sha256:a4cdc44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855": {}
63 | }
64 | }
65 | ```
66 |
67 | `myfeature` will be installed after `second`, `third`, `fourth`, and `fifth`.
68 |
69 | The following three sections will illustrate how an orchestrating tool shouuld identify dependencies between Features, depending on [how the Feature is referenced](https://containers.dev/implementors/features/#referencing-a-feature).
70 |
71 | #### Identifying Dependencies - OCI Registry
72 |
73 | To speed up dependency resolution, all Features [published to an OCI registry](https://containers.dev/implementors/features-distribution/#oci-registry) will have their entire `devcontainer-feature.json` serialized and added as an [annotation](https://github.com/opencontainers/image-spec/blob/main/annotations.md).
74 |
75 | The orchestrating tool can use this annotation to resolve the Feature's dependencies without having to download and extract the Feature's tarball.
76 |
77 | More specifically, an [annotation](https://github.com/opencontainers/image-spec/blob/main/annotations.md) named `dev.containers.metadata` will be populated on the manifest when published by an implementing tool. This annotation is the escaped JSON object of the entire `devcontainer-feature.json` as it appears during the [packaging stage](https://containers.dev/implementors/features-distribution/#packaging).
78 |
79 | An example manifest with the `dev.containers.metadata` annotation:
80 |
81 | ```json
82 | {
83 | "schemaVersion": 2,
84 | "mediaType": "application/vnd.oci.image.manifest.v1+json",
85 | "config": {
86 | "mediaType": "application/vnd.devcontainers",
87 | "digest": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
88 | "size": 0
89 | },
90 | "layers": [
91 | {
92 | "mediaType": "application/vnd.devcontainers.layer.v1+tar",
93 | "digest": "sha256:738af5504b253dc6de51d2cb1556cdb7ce70ab18b2f32b0c2f12650ed6d2e4bc",
94 | "size": 3584,
95 | "annotations": {
96 | "org.opencontainers.image.title": "devcontainer-feature-myFeature.tgz"
97 | }
98 | }
99 | ],
100 | "annotations": {
101 | "dev.containers.metadata": "{\"name\": \"My Feature\",\"id\": \"myFeature\",\"version\": \"1.0.0\",\"dependsOn\": {\"ghcr.io/myotherFeature:1\": {\"flag\": true},\"features.azurecr.io/aThirdFeature:1\": {},\"features.azurecr.io/aFourthFeature:1.2.3\": {}}}"
102 | }
103 | }
104 | ```
105 |
106 | > If no annotation is present on a Feature's manifest, the orchestrating tool MUST fallback to downloading and extracting the Feature's contents to read the metadata properties. Failure to do so may result in a Feature being installed without its dependencies (e.g. `installsAfter`).
107 |
108 | In summary, supporting tools may choose to identify the dependencies of the declared user-defined Features by fetching the manifest of the Features and reading the `dependsOn` and `installsAfter` properties from the `dev.containers.metadata` annotation. This will allow the orchestrating tool to recursively resolve all dependencies without having to download and extract each Feature's tarball.
109 |
110 | #### Identifying Dependencies - HTTPS Direct Tarball
111 |
112 | Published Features referenced directly by [HTTPS URI pointing to the packaged tarball archive](https://containers.dev/implementors/features-distribution/#directly-reference-tarball) will need to be fully downloaded an extracted to read the `dependsOn` property.
113 |
114 | #### Identifying Dependencies - Local Features
115 |
116 | [Local Features](https://containers.dev/implementors/features-distribution/#addendum-locally-referenced) dependencies are identified by directly reading the `dependsOn` property in the associated `devcontainer-feature.json` metadata file.
117 |
118 | ### (B) `dependsOn` install order algorithm
119 |
120 | The orchestrating tool is responsible for calculating a Feature installation order (or error, if no valid installation order can be resolved). The set of Features to be installed is the union of user-defined Features and their dependencies. The orchestrating tool will perform the following steps:
121 |
122 | #### **(B1) Building dependency graph**
123 |
124 | From the user-defined Features, the orchestrating tool will build a dependency graph. The graph will be built by traversing the `dependsOn` and `installsAfter` properties of each Feature. The metadata for each dependency is then fetched and the node added as an edge to to the dependent Feature. For `dependsOn` dependencies, the dependency will be fed back into the worklist to be recursively resolved.
125 |
126 | An accumulator is maintained with all uniquely discovered and user-provided Features, each with a reference to its dependencies. If the exact Feature (see **Feature Equality**) has already been added to the accumulator, it will not be added again. The accumulator will be fed into (B3) after the Feature tree has been resolved.
127 |
128 | The graph may be stored as an adjacency list with two kinds of edges (1) `dependsOn` edges or "hard dependencies" and (2) `installsAfter` edges or "soft dependencies".
129 |
130 | #### **(B2) Assigning `roundPriority`**
131 |
132 | Each node in the graph has an implicit, default `roundPriority` of 0.
133 |
134 | To influence installation order globally while still honoring the dependency graph of built in **(B1)**, `roundPriority` values may be tweaks for each Feature. When each round is calculated in **(B3)**, only the Features equal to the max `roundPriority` of that set will be committed (the remaining will be uncommitted and reevaulated in subsequent rounds).
135 |
136 | The `roundPriority` is set to a non-zero value in the following instances:
137 |
138 | - If the [`devcontainer.json` contains an `overrideFeatureInstallOrder`](#overridefeatureinstallorder).
139 |
140 | #### **(B3) Round-based sorting**
141 |
142 | Perform a sort on the result of **(B1)** in rounds. This sort will rearrange Features, producing a sorted list of Features to install. The sort will be performed as follows:
143 |
144 | Start with all the elements from **(B2)** in a `worklist` and an empty list `installationOrder`. While the `worklist` is not empty, iterate through each element in the `worklist` and check if all its dependencies (if any) are already members of `installationOrder`. If the check is true, add it to an intermediate list `round` If not, skip it. Equality is determined in **Feature Equality**.
145 |
146 | Then for each intermediate `round` list, commit to `installationOrder` only those nodes who share the maximum `roundPriority`. Return all nodes in `round` with a strictly lower `roundPriority` to the `worklist` to be reprocessed in subsequent iterations. If there are multiple nodes with the same `roundPriority`, commit them to `installationOrder` with a final sort according to **Round Stable Sort**.
147 |
148 | Repeat for as many rounds as necessary until `worklist` is empty. If there is ever a round where no elements are added to `installationOrder`, the algorithm should terminate and return an error. This indicates a circular dependency or other fatal error in the dependency graph. Implementations should attempt to provide the user with information about the error and possible mitigation strategies.
149 |
150 |
151 | #### Definition: Feature Equality
152 |
153 | This specification defines two Features as equal if both Features point to the same exact contents and are executed with the same options.
154 |
155 | **For Features published to an OCI registry**, two Feature are identical if their manifest digests are equal, and the options executed against the Feature are equal (compared value by value). Identical manifest digests implies that the tgz contents of the Feature and its entire `devcontainer-feature.json` are identical. If any of these conditions are not met, the Features are considered not equal.
156 |
157 | **For Features fetched by HTTPS URI**, two Features are identical if the contents of the tgz are identical (hash to the same value), and the options executed against the Feature are equal (compared value by value). If any of these conditions are not met, the Features are considered not equal.
158 |
159 | **For local Features**, each Feature is considered unique and not equal to any other local Feature.
160 |
161 | #### Definition: Round Stable Sort
162 |
163 | To prevent non-deterministic behavior, the algorithm will sort each **round** according to the following rules:
164 |
165 | - Compare and sort each Feature lexiographically by their fully qualified resource name (For OCI-published Features, that means the ID without version or digest.). If the comparison is equal:
166 | - Compare and sort each Feature from oldest to newest tag (`latest` being the "most new"). If the comparision is equal:
167 | - Compare and sort each Feature by their options by:
168 | - Greatest number of user-defined options (note omitting an option will default that value to the Feature's default value and is not considered a user-defined option). If the comparison is equal:
169 | - Sort the provided option keys lexicographically. If the comparison is equal:
170 | - Sort the provided option values lexicographically. If the comparision is equal:
171 | - Sort Features by their canonical name (For OCI-published Features, the Feature ID resolved to the digest hash).
172 |
173 | If there is no difference based on these comparator rules, the Features are considered equal.
174 |
175 | ## Note: Existing methods of altering Feature installation order
176 |
177 | Two existing properties (1) `installsAfter` on the Feature metadata, and (2) `overrideFeatureInstallationOrder` in the `devcontainer.json` both exist to alter the installation order of user-defined Features. The behavior of these properties are carried forward in this proposal.
178 |
179 | ### installsAfter
180 |
181 | The `installsAfter` property is a "soft dependency" that influences the installation order of Features that are queued to be installed. The effective behavior of this property is the same as `dependsOn`, with the following differences:
182 |
183 | - `installsAfter` is not recursive.
184 | - `installsAfter` only influences the installation order of Features that are already set to be installed after resolving the dependency tree.
185 | - The Feature indicated by `installsAfter` can not provide options, nor are they able to be pinned to a specific version tag or digest. This is unchanged from the current specification.
186 |
187 | From an implementation point of view, `installsAfter` nodes may be added as a seperate set of directed edges, just as `dependsOn` nodes are added as directed edges (see **(B1)**). Before round-based installation and sorting **(B3)**, an orchestrating tool should remove all `installsAfter` directed edges that do not correspond with a Feature in the `worklist` that is set to be installed. In each round, a Feature can then be installed if all its requirements (both `dependsOn` and `installsAfter` dependencies) have been fulfilled in previous rounds.
188 |
189 | An implemention should fail the dependency resolution step if the evaluation of the `installsAfter` property results in an inconsistent state (e.g. a circular dependency).
190 |
191 | ### overrideFeatureInstallOrder
192 |
193 | The `overrideFeatureInstallOrder` property of `devcontainer.json` is an array of Feature IDs that are to be installed in descending priority order as soon as its dependencies outlined above are installed.
194 |
195 | This evaluation is performed by assigning a [`roundPriority`](#b2-assigning-roundpriority) to all nodes that match the Feature identifier present in the property.
196 |
197 | For example, given `n` Features in the `overrideFeatureInstallOrder` array, the orchestrating tool should assign a `roundPriority` of `n - idx` to each Feature, where `idx` is the zero-based index of the Feature in the array.
198 |
199 | For example:
200 |
201 | ```json
202 | overrideFeatureInstallOrder = [
203 | "foo",
204 | "bar",
205 | "baz"
206 | ]
207 | ```
208 |
209 | would result in the following `roundPriority` assignments:
210 |
211 | ```json
212 | const roundPriority = {
213 | "foo": 3,
214 | "bar": 2,
215 | "baz": 1
216 | }
217 | ```
218 |
219 | This property must not influence the dependency relationship as defined by the dependency graph (see **(B1)**) and shall only be evaulated at the round-based sorting step (see **(B3)**). Put another way, this property cannot "pull forward" a Feature until all of its dependencies (both soft and hard) have been installed. After a Feature's dependencies have been installed in other rounds, this property should "pull forward" each Feature as early as possible (given the order of identifiers in the array).
220 |
221 | Similar to `installsAfter`, this property's members may not provide options, nor are they able to be pinned to a specific version tag or digest. This is unchanged from the current specification.
222 |
223 | If a Feature is indicated in `overrideFeatureInstallOrder` but not a member of the dependency graph (i.e. it is not queued to be installed), the orchestrating tool may fail the dependency resolution step.
224 |
225 | ## Additional Remarks
226 |
227 | ### Feature authorship
228 |
229 | Features should be authored with the following considerations:
230 |
231 | - Features should be authored with the assumption that they will be installed in any order, so long as the dependencies are met at some point beforehand.
232 | - Since two Features with different options are considered different, a single Feature may be installed more than once. Features should be idempotent.
233 | - Features that require updating shared state in the container (e.g. updating the `$PATH`), should be aware that the same Feature may be run multiple times. Consider a method for previous runs of the Feature to communicate with future runs, updating the shared state in the intended way.
234 |
235 |
236 | ### Image Metadata
237 |
238 | Dependency resolution is only performed on initial container creation by reading the `features` object of `devcontainer.json` and resolving the Features in that point in time. For subsequent creations from an image (or resumes of a dev container), the dependency tree is **not** re-calculated. To re-calculate the dependency tree, the user must delete the image (or dev container) and recreate it from a `devcontainer.json`.
239 |
240 | Once Feature dependencies are resolved, they are treated the same as if members of the user-defined Features. That means both user-defined Features and their dependencies are stored in the image's metadata.
241 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Attribution 4.0 International
2 |
3 | =======================================================================
4 |
5 | Creative Commons Corporation ("Creative Commons") is not a law firm and
6 | does not provide legal services or legal advice. Distribution of
7 | Creative Commons public licenses does not create a lawyer-client or
8 | other relationship. Creative Commons makes its licenses and related
9 | information available on an "as-is" basis. Creative Commons gives no
10 | warranties regarding its licenses, any material licensed under their
11 | terms and conditions, or any related information. Creative Commons
12 | disclaims all liability for damages resulting from their use to the
13 | fullest extent possible.
14 |
15 | Using Creative Commons Public Licenses
16 |
17 | Creative Commons public licenses provide a standard set of terms and
18 | conditions that creators and other rights holders may use to share
19 | original works of authorship and other material subject to copyright
20 | and certain other rights specified in the public license below. The
21 | following considerations are for informational purposes only, are not
22 | exhaustive, and do not form part of our licenses.
23 |
24 | Considerations for licensors: Our public licenses are
25 | intended for use by those authorized to give the public
26 | permission to use material in ways otherwise restricted by
27 | copyright and certain other rights. Our licenses are
28 | irrevocable. Licensors should read and understand the terms
29 | and conditions of the license they choose before applying it.
30 | Licensors should also secure all rights necessary before
31 | applying our licenses so that the public can reuse the
32 | material as expected. Licensors should clearly mark any
33 | material not subject to the license. This includes other CC-
34 | licensed material, or material used under an exception or
35 | limitation to copyright. More considerations for licensors:
36 | wiki.creativecommons.org/Considerations_for_licensors
37 |
38 | Considerations for the public: By using one of our public
39 | licenses, a licensor grants the public permission to use the
40 | licensed material under specified terms and conditions. If
41 | the licensor's permission is not necessary for any reason--for
42 | example, because of any applicable exception or limitation to
43 | copyright--then that use is not regulated by the license. Our
44 | licenses grant only permissions under copyright and certain
45 | other rights that a licensor has authority to grant. Use of
46 | the licensed material may still be restricted for other
47 | reasons, including because others have copyright or other
48 | rights in the material. A licensor may make special requests,
49 | such as asking that all changes be marked or described.
50 | Although not required by our licenses, you are encouraged to
51 | respect those requests where reasonable. More_considerations
52 | for the public:
53 | wiki.creativecommons.org/Considerations_for_licensees
54 |
55 | =======================================================================
56 |
57 | Creative Commons Attribution 4.0 International Public License
58 |
59 | By exercising the Licensed Rights (defined below), You accept and agree
60 | to be bound by the terms and conditions of this Creative Commons
61 | Attribution 4.0 International Public License ("Public License"). To the
62 | extent this Public License may be interpreted as a contract, You are
63 | granted the Licensed Rights in consideration of Your acceptance of
64 | these terms and conditions, and the Licensor grants You such rights in
65 | consideration of benefits the Licensor receives from making the
66 | Licensed Material available under these terms and conditions.
67 |
68 |
69 | Section 1 -- Definitions.
70 |
71 | a. Adapted Material means material subject to Copyright and Similar
72 | Rights that is derived from or based upon the Licensed Material
73 | and in which the Licensed Material is translated, altered,
74 | arranged, transformed, or otherwise modified in a manner requiring
75 | permission under the Copyright and Similar Rights held by the
76 | Licensor. For purposes of this Public License, where the Licensed
77 | Material is a musical work, performance, or sound recording,
78 | Adapted Material is always produced where the Licensed Material is
79 | synched in timed relation with a moving image.
80 |
81 | b. Adapter's License means the license You apply to Your Copyright
82 | and Similar Rights in Your contributions to Adapted Material in
83 | accordance with the terms and conditions of this Public License.
84 |
85 | c. Copyright and Similar Rights means copyright and/or similar rights
86 | closely related to copyright including, without limitation,
87 | performance, broadcast, sound recording, and Sui Generis Database
88 | Rights, without regard to how the rights are labeled or
89 | categorized. For purposes of this Public License, the rights
90 | specified in Section 2(b)(1)-(2) are not Copyright and Similar
91 | Rights.
92 |
93 | d. Effective Technological Measures means those measures that, in the
94 | absence of proper authority, may not be circumvented under laws
95 | fulfilling obligations under Article 11 of the WIPO Copyright
96 | Treaty adopted on December 20, 1996, and/or similar international
97 | agreements.
98 |
99 | e. Exceptions and Limitations means fair use, fair dealing, and/or
100 | any other exception or limitation to Copyright and Similar Rights
101 | that applies to Your use of the Licensed Material.
102 |
103 | f. Licensed Material means the artistic or literary work, database,
104 | or other material to which the Licensor applied this Public
105 | License.
106 |
107 | g. Licensed Rights means the rights granted to You subject to the
108 | terms and conditions of this Public License, which are limited to
109 | all Copyright and Similar Rights that apply to Your use of the
110 | Licensed Material and that the Licensor has authority to license.
111 |
112 | h. Licensor means the individual(s) or entity(ies) granting rights
113 | under this Public License.
114 |
115 | i. Share means to provide material to the public by any means or
116 | process that requires permission under the Licensed Rights, such
117 | as reproduction, public display, public performance, distribution,
118 | dissemination, communication, or importation, and to make material
119 | available to the public including in ways that members of the
120 | public may access the material from a place and at a time
121 | individually chosen by them.
122 |
123 | j. Sui Generis Database Rights means rights other than copyright
124 | resulting from Directive 96/9/EC of the European Parliament and of
125 | the Council of 11 March 1996 on the legal protection of databases,
126 | as amended and/or succeeded, as well as other essentially
127 | equivalent rights anywhere in the world.
128 |
129 | k. You means the individual or entity exercising the Licensed Rights
130 | under this Public License. Your has a corresponding meaning.
131 |
132 |
133 | Section 2 -- Scope.
134 |
135 | a. License grant.
136 |
137 | 1. Subject to the terms and conditions of this Public License,
138 | the Licensor hereby grants You a worldwide, royalty-free,
139 | non-sublicensable, non-exclusive, irrevocable license to
140 | exercise the Licensed Rights in the Licensed Material to:
141 |
142 | a. reproduce and Share the Licensed Material, in whole or
143 | in part; and
144 |
145 | b. produce, reproduce, and Share Adapted Material.
146 |
147 | 2. Exceptions and Limitations. For the avoidance of doubt, where
148 | Exceptions and Limitations apply to Your use, this Public
149 | License does not apply, and You do not need to comply with
150 | its terms and conditions.
151 |
152 | 3. Term. The term of this Public License is specified in Section
153 | 6(a).
154 |
155 | 4. Media and formats; technical modifications allowed. The
156 | Licensor authorizes You to exercise the Licensed Rights in
157 | all media and formats whether now known or hereafter created,
158 | and to make technical modifications necessary to do so. The
159 | Licensor waives and/or agrees not to assert any right or
160 | authority to forbid You from making technical modifications
161 | necessary to exercise the Licensed Rights, including
162 | technical modifications necessary to circumvent Effective
163 | Technological Measures. For purposes of this Public License,
164 | simply making modifications authorized by this Section 2(a)
165 | (4) never produces Adapted Material.
166 |
167 | 5. Downstream recipients.
168 |
169 | a. Offer from the Licensor -- Licensed Material. Every
170 | recipient of the Licensed Material automatically
171 | receives an offer from the Licensor to exercise the
172 | Licensed Rights under the terms and conditions of this
173 | Public License.
174 |
175 | b. No downstream restrictions. You may not offer or impose
176 | any additional or different terms or conditions on, or
177 | apply any Effective Technological Measures to, the
178 | Licensed Material if doing so restricts exercise of the
179 | Licensed Rights by any recipient of the Licensed
180 | Material.
181 |
182 | 6. No endorsement. Nothing in this Public License constitutes or
183 | may be construed as permission to assert or imply that You
184 | are, or that Your use of the Licensed Material is, connected
185 | with, or sponsored, endorsed, or granted official status by,
186 | the Licensor or others designated to receive attribution as
187 | provided in Section 3(a)(1)(A)(i).
188 |
189 | b. Other rights.
190 |
191 | 1. Moral rights, such as the right of integrity, are not
192 | licensed under this Public License, nor are publicity,
193 | privacy, and/or other similar personality rights; however, to
194 | the extent possible, the Licensor waives and/or agrees not to
195 | assert any such rights held by the Licensor to the limited
196 | extent necessary to allow You to exercise the Licensed
197 | Rights, but not otherwise.
198 |
199 | 2. Patent and trademark rights are not licensed under this
200 | Public License.
201 |
202 | 3. To the extent possible, the Licensor waives any right to
203 | collect royalties from You for the exercise of the Licensed
204 | Rights, whether directly or through a collecting society
205 | under any voluntary or waivable statutory or compulsory
206 | licensing scheme. In all other cases the Licensor expressly
207 | reserves any right to collect such royalties.
208 |
209 |
210 | Section 3 -- License Conditions.
211 |
212 | Your exercise of the Licensed Rights is expressly made subject to the
213 | following conditions.
214 |
215 | a. Attribution.
216 |
217 | 1. If You Share the Licensed Material (including in modified
218 | form), You must:
219 |
220 | a. retain the following if it is supplied by the Licensor
221 | with the Licensed Material:
222 |
223 | i. identification of the creator(s) of the Licensed
224 | Material and any others designated to receive
225 | attribution, in any reasonable manner requested by
226 | the Licensor (including by pseudonym if
227 | designated);
228 |
229 | ii. a copyright notice;
230 |
231 | iii. a notice that refers to this Public License;
232 |
233 | iv. a notice that refers to the disclaimer of
234 | warranties;
235 |
236 | v. a URI or hyperlink to the Licensed Material to the
237 | extent reasonably practicable;
238 |
239 | b. indicate if You modified the Licensed Material and
240 | retain an indication of any previous modifications; and
241 |
242 | c. indicate the Licensed Material is licensed under this
243 | Public License, and include the text of, or the URI or
244 | hyperlink to, this Public License.
245 |
246 | 2. You may satisfy the conditions in Section 3(a)(1) in any
247 | reasonable manner based on the medium, means, and context in
248 | which You Share the Licensed Material. For example, it may be
249 | reasonable to satisfy the conditions by providing a URI or
250 | hyperlink to a resource that includes the required
251 | information.
252 |
253 | 3. If requested by the Licensor, You must remove any of the
254 | information required by Section 3(a)(1)(A) to the extent
255 | reasonably practicable.
256 |
257 | 4. If You Share Adapted Material You produce, the Adapter's
258 | License You apply must not prevent recipients of the Adapted
259 | Material from complying with this Public License.
260 |
261 |
262 | Section 4 -- Sui Generis Database Rights.
263 |
264 | Where the Licensed Rights include Sui Generis Database Rights that
265 | apply to Your use of the Licensed Material:
266 |
267 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right
268 | to extract, reuse, reproduce, and Share all or a substantial
269 | portion of the contents of the database;
270 |
271 | b. if You include all or a substantial portion of the database
272 | contents in a database in which You have Sui Generis Database
273 | Rights, then the database in which You have Sui Generis Database
274 | Rights (but not its individual contents) is Adapted Material; and
275 |
276 | c. You must comply with the conditions in Section 3(a) if You Share
277 | all or a substantial portion of the contents of the database.
278 |
279 | For the avoidance of doubt, this Section 4 supplements and does not
280 | replace Your obligations under this Public License where the Licensed
281 | Rights include other Copyright and Similar Rights.
282 |
283 |
284 | Section 5 -- Disclaimer of Warranties and Limitation of Liability.
285 |
286 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
287 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
288 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
289 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
290 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
291 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
292 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
293 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
294 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
295 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
296 |
297 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
298 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
299 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
300 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
301 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
302 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
303 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
304 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
305 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
306 |
307 | c. The disclaimer of warranties and limitation of liability provided
308 | above shall be interpreted in a manner that, to the extent
309 | possible, most closely approximates an absolute disclaimer and
310 | waiver of all liability.
311 |
312 |
313 | Section 6 -- Term and Termination.
314 |
315 | a. This Public License applies for the term of the Copyright and
316 | Similar Rights licensed here. However, if You fail to comply with
317 | this Public License, then Your rights under this Public License
318 | terminate automatically.
319 |
320 | b. Where Your right to use the Licensed Material has terminated under
321 | Section 6(a), it reinstates:
322 |
323 | 1. automatically as of the date the violation is cured, provided
324 | it is cured within 30 days of Your discovery of the
325 | violation; or
326 |
327 | 2. upon express reinstatement by the Licensor.
328 |
329 | For the avoidance of doubt, this Section 6(b) does not affect any
330 | right the Licensor may have to seek remedies for Your violations
331 | of this Public License.
332 |
333 | c. For the avoidance of doubt, the Licensor may also offer the
334 | Licensed Material under separate terms or conditions or stop
335 | distributing the Licensed Material at any time; however, doing so
336 | will not terminate this Public License.
337 |
338 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
339 | License.
340 |
341 |
342 | Section 7 -- Other Terms and Conditions.
343 |
344 | a. The Licensor shall not be bound by any additional or different
345 | terms or conditions communicated by You unless expressly agreed.
346 |
347 | b. Any arrangements, understandings, or agreements regarding the
348 | Licensed Material not stated herein are separate from and
349 | independent of the terms and conditions of this Public License.
350 |
351 |
352 | Section 8 -- Interpretation.
353 |
354 | a. For the avoidance of doubt, this Public License does not, and
355 | shall not be interpreted to, reduce, limit, restrict, or impose
356 | conditions on any use of the Licensed Material that could lawfully
357 | be made without permission under this Public License.
358 |
359 | b. To the extent possible, if any provision of this Public License is
360 | deemed unenforceable, it shall be automatically reformed to the
361 | minimum extent necessary to make it enforceable. If the provision
362 | cannot be reformed, it shall be severed from this Public License
363 | without affecting the enforceability of the remaining terms and
364 | conditions.
365 |
366 | c. No term or condition of this Public License will be waived and no
367 | failure to comply consented to unless expressly agreed to by the
368 | Licensor.
369 |
370 | d. Nothing in this Public License constitutes or may be interpreted
371 | as a limitation upon, or waiver of, any privileges and immunities
372 | that apply to the Licensor or You, including from the legal
373 | processes of any jurisdiction or authority.
374 |
375 |
376 | =======================================================================
377 |
378 | Creative Commons is not a party to its public
379 | licenses. Notwithstanding, Creative Commons may elect to apply one of
380 | its public licenses to material it publishes and in those instances
381 | will be considered the “Licensor.” The text of the Creative Commons
382 | public licenses is dedicated to the public domain under the CC0 Public
383 | Domain Dedication. Except for the limited purpose of indicating that
384 | material is shared under a Creative Commons public license or as
385 | otherwise permitted by the Creative Commons policies published at
386 | creativecommons.org/policies, Creative Commons does not authorize the
387 | use of the trademark "Creative Commons" or any other trademark or logo
388 | of Creative Commons without its prior written consent including,
389 | without limitation, in connection with any unauthorized modifications
390 | to any of its public licenses or any other arrangements,
391 | understandings, or agreements concerning use of licensed material. For
392 | the avoidance of doubt, this paragraph does not form part of the
393 | public licenses.
394 |
395 | Creative Commons may be contacted at creativecommons.org.
--------------------------------------------------------------------------------
/docs/specs/devcontainer-reference.md:
--------------------------------------------------------------------------------
1 | # Dev Container Specification
2 |
3 | The purpose of the **Development Container Specification** is to provide a way to enrich containers with the content and metadata necessary to enable development inside them. These container **environments** should be easy to use, create, and recreate.
4 |
5 | A **development container** is a container in which a user can develop an application. Tools that want to implement this specification should provide a set of features/commands that give more flexibility to users and allow **development containers** to scale to large development groups.
6 |
7 | An **environment** is defined as a logical instance of one or more **development containers**, along with any needed side-car containers. An environment is based on one set of metadata that can be managed as a single unit. Users can create multiple **environments** from the same configuration metadata for different purposes.
8 |
9 | # Metadata
10 |
11 | Development containers allow one to define a repeatable development environment for a user or team of developers that includes the execution environment the application needs. A development container defines an environment in which you develop your application before you are ready to deploy. While deployment and development containers may resemble one another, you may not want to include tools in a deployment image that you use during development and you may need to use different secrets or other settings.
12 |
13 | Furthermore, working inside a development container can require additional **metadata** to drive tooling or service experiences than you would normally need with a production container. Providing a structured and consistent form for this metadata is a core part of this specification.
14 |
15 | ## devcontainer.json
16 |
17 | While the structure of this metadata is critical, it is also important to call out how this data can be represented on disk where appropriate. While other representations may be added over time, metadata can be stored in a JSON with Comments file called `devcontainer.json` today. Products using it should expect to find a `devcontainer.json` file in one or more of the following locations (in order of precedence):
18 |
19 | - .devcontainer/devcontainer.json
20 | - .devcontainer.json
21 | - .devcontainer/``/devcontainer.json (where `` is a sub-folder, one level deep)
22 |
23 | It is valid that these files may exist in more than one location, so consider providing a mechanism for users to select one when appropriate.
24 |
25 | ## Image Metadata
26 |
27 | Certain dev container metadata properties can be stored in an image label as an array of metadata snippets. This allows them to be stored in prebuilt images, such that, the image and its related configuration are self-contained. These contents should then be merged with any local `devcontainer.json` file contents at the time the container is created. An array is used so subsequent image builds can simply append changes to the array rather than attempting to merge at that point - which improves compatibility with arbitrary image build systems.
28 |
29 | Metadata should be representative of with the following structure, using one entry per [Dev Container Feature](../features) and `devcontainer.json` (see table below for the full list):
30 |
31 | ```json
32 | [
33 | {
34 | "id"?: string,
35 | "init"?: boolean,
36 | "privileged"?: boolean,
37 | "capAdd"?: string[],
38 | "securityOpt"?: string[],
39 | "entrypoint"?: string,
40 | "mounts"?: [],
41 | ...
42 | "customizations"?: {
43 | ...
44 | }
45 | },
46 | ...
47 | ]
48 | ```
49 |
50 | To simplify adding this metadata for other tools, we also support having a single top-level object with the same properties.
51 |
52 | The metadata is added to the image as a `devcontainer.metadata` label with a JSON string value representing the above array or single object.
53 |
54 | ### Merge Logic
55 |
56 | To apply the metadata together with a user's `devcontainer.json` at runtime the following merge logic by property is used. The table also notes which properties are currently supported coming from the `devcontainer.json` and which from the Feature metadata, this will change over time as we add more properties.
57 |
58 | | Property | Type/Format | Merge Logic | devcontainer.json | devcontainer-feature.json |
59 | | -------- | ----------- | ----------- | :---------------: | :--------------: |
60 | | `id` | E.g., `ghcr.io/devcontainers/features/node:1` | Not merged. | | ✓ |
61 | | `init` | `boolean` | `true` if at least one is `true`, `false` otherwise. | ✓ | ✓ |
62 | | `privileged` | `boolean` | `true` if at least one is `true`, `false` otherwise. | ✓ | ✓ |
63 | | `capAdd` | `string[]` | Union of all `capAdd` arrays without duplicates. | ✓ | ✓ |
64 | | `securityOpt` | `string[]` | Union of all `securityOpt` arrays without duplicates. | ✓ | ✓ |
65 | | `entrypoint` | `string` | Collected list of all entrypoints. | | ✓ |
66 | | `mounts` | `(string \| { type, src, dst })[]` | Collected list of all mountpoints. Conflicts: Last source wins. | ✓ | ✓ |
67 | | `onCreateCommand` | `string \| string[] \| {[key: string]: string \| string[]}` | Collected list of all onCreateCommands. | ✓ | ✓ |
68 | | `updateContentCommand` | `string \| string[] \| {[key: string]: string \| string[]}` | Collected list of all updateContentCommands. | ✓ | ✓ |
69 | | `postCreateCommand` | `string \| string[] \| {[key: string]: string \| string[]}` | Collected list of all postCreateCommands. | ✓ | ✓ |
70 | | `postStartCommand` | `string \| string[] \| {[key: string]: string \| string[]}` | Collected list of all postStartCommands. | ✓ | ✓ |
71 | | `postAttachCommand` | `string \| string[] \| {[key: string]: string \| string[]}` | Collected list of all postAttachCommands. | ✓ | ✓ |
72 | | `waitFor` | enum | Last value wins. | ✓ | |
73 | | `customizations` | Object of tool-specific customizations. | Merging is left to the tools. | ✓ | ✓ |
74 | | `containerUser` | `string` | Last value wins. | ✓ | |
75 | | `remoteUser` | `string` | Last value wins. | ✓ | |
76 | | `userEnvProbe` | `string` (enum) | Last value wins. | ✓ | |
77 | | `remoteEnv` | Object of strings. | Per variable, last value wins. | ✓ | |
78 | | `containerEnv` | Object of strings. | Per variable, last value wins. | ✓ | |
79 | | `overrideCommand` | `boolean` | Last value wins. | ✓ | |
80 | | `portsAttributes` | Map of ports to attributes. | Per port (not per port attribute), last value wins. | ✓ | |
81 | | `otherPortsAttributes` | Port attributes. | Last value wins (not per port attribute). | ✓ | |
82 | | `forwardPorts` | `(number \| string)[]` | Union of all ports without duplicates. Last one wins (when mapping changes). | ✓ | |
83 | | `shutdownAction` | `string` (enum) | Last value wins. | ✓ | |
84 | | `updateRemoteUserUID` | `boolean` | Last value wins. | ✓ | |
85 | | `hostRequirements` | `cpus`, `memory`, `storage`, `gpu` | Max value wins. | ✓ | |
86 |
87 | Variables in string values will be substituted at the time the value is applied. When the order matters, the `devcontainer.json` is considered last.
88 |
89 | ### Notes
90 |
91 | - Passing the label as a `LABEL` instruction in the Dockerfile:
92 | - The size limit on Dockerfiles is around 1.3MB. The line length is limited to 65k characters.
93 | - Using one line per Feature should allow for making full use of these limits.
94 | - Passing the label as a command line argument:
95 | - There is no size limit documented for labels, but the daemon returns an error when the request header is >500kb.
96 | - The 500kb limit is shared, so we cannot use a second label in the same build to avoid it.
97 | - If/when this becomes an issue we could embed the metadata as a file in the image (e.g. with a label indicating it).
98 |
99 | # Orchestration options
100 |
101 | A core principle of this specification is to seek to enrich existing container orchestrator formats with development container metadata where appropriate rather than replacing them. As a result, the metadata schema includes a set of optional properties for interoperating with different orchestrators. Today, the specification includes scenario-specific properties for working without a container orchestrator (by directly referencing an image or Dockerfile) and for using Docker Compose as a simple multi-container orchestrator. At the same time, this specification leaves space for further development and implementation of other orchestrator mechanisms and file formats.
102 |
103 | The following section describes the differences between those that are supported now.
104 |
105 | ## Image based
106 |
107 | Image based configurations only reference an image that should be reachable and downloadable through `docker pull` commands. Logins and tokens required for these operations are execution environment specific. The only required parameter is `image`. The details are [here](devcontainerjson-reference.md#image-or-dockerfile-specific-properties).
108 |
109 | ## Dockerfile based
110 |
111 | These configurations are defined as using a `Dockerfile` to define the starting point of the development containers. As with image based configurations, it is assumed that any base images are already reachable by **Docker** when performing a `docker build` command. The only required parameter in this case is the relative reference to the `Dockerfile` in `build.dockerfile`. The details are [here](devcontainerjson-reference.md#image-or-dockerfile-specific-properties).
112 |
113 | There are multiple properties that allow users to control how `docker build` works:
114 |
115 | - `build.context`
116 | - `build.args`
117 | - `build.options`
118 | - `build.target`
119 | - `build.cacheFrom`
120 |
121 | ## Docker Compose based
122 |
123 | Docker Compose configurations use `docker-compose` (which may be Docker Compose V1 or aliased Docker Compose V2) to create and manage a set of containers required for an application. As with the other configurations, any images required for this operation are assumed to be reachable. The required parameters are:
124 |
125 | - `dockerComposeFile`: the reference to the Docker Compose file(s) to be used.
126 | - `service`: declares the **main** container that will be used for all other operations. Tools are assumed to also use this parameter to connect to the development container, although they can provide facilities to connect to the other containers as required by the user.
127 | - `runServices`: an optional property that indicates the set of services in the `docker-compose` configuration that should be started or stopped with the environment.
128 |
129 | It is important to note that `image` and `dockerfile` properties are not needed since Docker Compose supports them natively in the format.
130 |
131 | # Other options
132 |
133 | In addition to the configuration options explained above, there are other settings that apply when creating development containers to facilitate their use by developers.
134 |
135 | A complete list of available metadata properties and their purposes can be found in the [`devcontainer.json` reference](devcontainerjson-reference.md). However, we will describe the critical ones below in more detail.
136 |
137 | ## Environment Variables
138 |
139 | Environment variables can be set at different points in the dev container lifecycle. With this in mind, **development containers** support two classes of environment variables:
140 |
141 | * **Container**: These variables are part of the container when it is created and are available at all points in its lifecycle. This concept is native to containers and can be set in the container image itself, using `containerEnv` for **image** and **Dockerfile** scenarios or using orchestrator specific properties like `env` in **Docker Compose** files.
142 | * **Remote**: These variables should be set by a **development container** supporting tool as part of configuring its runtime environment. Users can set these using the `remoteEnv` property and implementing tools or services may add their own for specific scenarios (e.g. secrets). These variables can change during the lifetime of the container, and are added after the container's `ENTRYPOINT` has fired.
143 |
144 | The reason for this separation is it allows for the use of information not available at image build time and simplifies updating the environment for project/repository specific needs without modifying an image. With this in mind, it's important to note that implementing tools should also support the [dynamic variable syntax](devcontainerjson-reference.md#variables-in-devcontainerjson) described in the metadata reference document.
145 |
146 | Another notable and important environment variable related property is **`userEnvProbe`**. Implementing tools should use this property to "probe" for expected environment variables using the specified type of shell. However, it does not specify that this type of shell needs to be used for all sub-processes (given the performance impact). Instead, "probed" environment variables should be merged with Remote environment variables for any processes the implementer injects after the container is created. This allows implementors to emulate developer expected behaviors around values added to their profile and rc files.
147 |
148 | ## Mounts
149 |
150 | Mounts allow containers to have access to the underlying machine, share data between containers and to persist information between development containers.
151 |
152 | A default mount should be included so that the source code is accessible from inside the container. Source code is stored outside of the container so that a developer's in-flight edits can be extracted, or a new container created in the event a container no longer starts.
153 |
154 | While this "workspace mount" is often a "bind" mount, this is not a requirement of this specification. It is also important to note that these mounts may be "bind" mounts that connect to the underlying filesystem and thus cloud environments might not have access to the same data as a local machine.
155 |
156 | Inside the container this mount defaults to `/workspace`.
157 |
158 | ## workspaceFolder and workspaceMount
159 |
160 | The default mount point for the source code can be set with the `workspaceMount` property for **image** and **dockerfile** scenarios or using the built in `mounts` property in **Docker Compose** files. This folder should point to the root of a repository (where the `.git` folder is found) so that source control operations work correctly inside the container.
161 |
162 | The `workspaceFolder` can then be set to the default folder inside the container that should used in the container. Typically this is either the mount point in the container, or a sub-folder under it. Allowing a sub-folder to be used is particularly important for monorepos given you need the `.git` folder to interact with source control but developers are typically interacting with a specific sub-project within the overall repository.
163 |
164 | See [`workspaceMount` and `workspaceFolder`](devcontainerjson-reference.md#image-or-dockerfile-specific-properties) for reference.
165 |
166 | ## Users
167 |
168 | Users control the permissions of applications executed in the containers, allowing the developer to control them. The specification takes into account two types of user definitions:
169 |
170 | * **Container User**: The user that will be used for all operations that run inside a container. This concept is native to containers. It may be set in the container image, using the `containerUser` property for **image** and **dockerfile** scenarios, or using an orchestratric specific property like `user` property in Docker Compose files.
171 | * **Remote User**: Used to run the [lifecycle](#lifecycle) scripts inside the container. This is also the user tools and editors that connect to the container should use to run their processes. This concept is not native to containers. Set using the `remoteUser` property in all cases and defaults to the container user.
172 |
173 | This separation allows the ENTRYPOINT for the image to execute with different permissions than the developer and allows for developers to switch users without recreating their containers.
174 |
175 | # Lifecycle
176 |
177 | A development environment goes through different lifecycle events during its use in the outer and inner loop of development.
178 |
179 | - Configuration Validation
180 | - Environment Creation
181 | - Environment Stop
182 | - Environment Resume
183 |
184 | ## Configuration Validation
185 |
186 | The exact steps required to validate configuration can vary based on exactly where the development container metadata is persisted. However, when considering a `devcontainer.json` file, the following validation should occur:
187 |
188 | 1. Validate that a workspace source folder has been provided. It is up to the implementing tool to determine what to do if no source folder is provided.
189 | 2. Search for a `devcontainer.json` file in one of the locations [above](#devcontainerjson) in the workspace source folder.
190 | 3. If no `devcontainer.json` is found, it is up to the implementing tool or service to determine what to do. This specification does not dictate this behavior.
191 | 4. Validate that the metadata (for example `devcontainer.json`) contains all parameters required for the selected configuration type.
192 |
193 | ## Environment Creation
194 |
195 | The creation process goes through the steps necesarry to go from the user configuration to a working **environment** that is ready to be used.
196 |
197 | ### Initialization
198 |
199 | During this step, the following is executed:
200 | - Validate access to the container orchestrator specified by the configuration.
201 | - Execution of `initializeCommand`.
202 |
203 | ### Image creation
204 |
205 | The first part of environment creation is generating the final image(s) that the development containers are going to use. This step is orchestrator dependent and can consist of just pulling a Docker image, running Docker build, or docker-compose build. Additionally, this step is useful on its own since it permits the creation of intermediate images that can be uploaded and used by other users, thus cutting down on creation time. It is encouraged that tools implementing this specification give access to a command that just executes this step.
206 |
207 | This step executes the following tasks:
208 |
209 | 1. [Configuration Validation](#configuration-validation)
210 | 2. Pull/build/execute of the defined container orchestration format to create images.
211 | 3. Validate the result of these operations.
212 |
213 | ### Container Creation
214 |
215 | After image creation, containers are created based on that image and setup.
216 |
217 | This step executes the following tasks:
218 |
219 | 1. [Optional] Perform any required user UID/GID sync'ing (more next)
220 | 2. Create the container(s) based on the properties specified above.
221 | 3. Validate the container(s) were created successfully.
222 |
223 | Note that container [mounts](#mounts), [environment variables](#environment-variables), and [user](#users) configuration should be applied at this point. However, remote user and environment variable configuration should **not** be.
224 |
225 | UID/GID sync'ing is an optional task for Linux (only) and that executes if the `updateRemoteUserUID` property is set to true and a `containerUser` or `remoteUser` is specified. In this case, an image update should be made prior to creating the container to set the specified user's UID and GID to match the current local user’s UID/GID to avoid permission problems with bind mounts. Implementations **may** skip this task if they do not use bind mounts on Linux, or use a container engine that does this translation automatically.
226 |
227 | ### Post Container Creation
228 |
229 | At the end of the container creation step, a set of commands are executed inside the **main** container:
230 | - `onCreateCommand`, `updateContentCommand` and `postCreateCommand`. This set of commands is executed in sequence on a container the first time it's created and depending on the creation parameters received. You can learn more in the [documentation on lifecycle scripts](devcontainerjson-reference.md#lifecycle-scripts). By default, `postCreateCommand` is executed in the background after reporting the successful creation of the development environment.
231 | - If the `waitFor` property is defined, then execution should block until all commands in the sequence up to the specified property have executed. This property defaults to `updateContentCommand`.
232 |
233 | Remote [environment variables](#environment-variables) and [user](#users) configuration should be applied to all created processes in the container (inclusive of `userEnvProbe`).
234 |
235 | ### Implementation specific steps
236 |
237 | After these steps have been executed, any implementation specific commands can safely execute. Specifically, any processes required by the implementation to support other properties in this specification should be started at this point. These may occur in parallel to any non-blocking, background post-container creation commands (as dictated by the `waitFor` property).
238 |
239 | Any user facing processes should have remote [environment variables](#environment-variables) and [user](#users) configuration applied (inclusive of `userEnvProbe`).
240 |
241 | For example, in the [CLI reference implementation](https://github.com/devcontainers/cli), this is the point in which anything executed with `devcontainer exec` would run.
242 |
243 | Typically, this is also the step where implementors would apply config or settings from the `customizations` section of the dev container metadata (e.g. VS Code installs extensions based on the `customizations.vscode.extensions` property). Examples of these can be found in the [supporting tools section](supporting-tools.md) reference. However, applying these at this point is not strictly required or mandated by this specification.
244 |
245 | Once these final steps have occurred, implementing tools or services may connect to the environment as they see fit.
246 |
247 | ## Environment Stop
248 |
249 | The intention of this step is to ensure all containers are stopped correctly based on the appropriate orchestrator specific steps to ensure no data is lost. It is up to the implementing tool or service to determine when this event should happen.
250 |
251 | ## Environment Resume
252 |
253 | While it is not a strict requirement to keep a **development container** after it has been stopped, this is the most common scenario.
254 |
255 | To resume the environment from a stopped state:
256 |
257 | 1. Restart all related containers.
258 | 2. Follow the appropriate [implementation specific steps](#implementation-specific-steps).
259 | 3. Additionally, execute the `postStartCommand` and `postAttachCommand` in the container.
260 |
261 | Like during the create process, remote [environment variables](#environment-variables) and [user](#users) configuration should be applied to all created processes in the container (inclusive of `userEnvProbe`).
262 |
263 | ## Parallel lifecycle script execution
264 |
265 | Dev containers support a single command for each of its lifecycle scripts. While serial execution of multiple commands can be achieved with `;`, `&&`, etc., parallel execution deserves first-class support.
266 |
267 | All lifecycle scripts have been extended to support `object` types. The key of the `object` will be a unique name for the command and the value will be the `string` or `array` command. Each command must exit successfully for the stage to be considered successful.
268 |
269 | Each entry in the `object` will be run in parallel during that lifecycle step.
270 |
271 | ### Example
272 |
273 | ```json
274 | {
275 | "postCreateCommand": {
276 | "server": "npm start",
277 | "db": ["mysql", "-u", "root", "-p", "my database"]
278 | }
279 | }
280 | ```
281 |
282 | # Definitions
283 |
284 | #### **Project Workspace Folder**
285 |
286 | The **project workspace folder** is where an implementing tool should begin to search for `devcontainer.json` files. If the target project on disk is using git, the **project workspace folder** is typically the root of the git repository.
287 |
--------------------------------------------------------------------------------
/schemas/devContainer.base.schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2019-09/schema",
3 | "description": "Defines a dev container",
4 | "allowComments": true,
5 | "allowTrailingCommas": false,
6 | "definitions": {
7 | "devContainerCommon": {
8 | "type": "object",
9 | "properties": {
10 | "$schema": {
11 | "type": "string",
12 | "format": "uri",
13 | "description": "The JSON schema of the `devcontainer.json` file."
14 | },
15 | "name": {
16 | "type": "string",
17 | "description": "A name for the dev container which can be displayed to the user."
18 | },
19 | "features": {
20 | "type": "object",
21 | "description": "Features to add to the dev container.",
22 | "properties": {
23 | "fish": {
24 | "deprecated": true,
25 | "deprecationMessage": "Legacy feature not supported. Please check https://containers.dev/features for replacements."
26 | },
27 | "maven": {
28 | "deprecated": true,
29 | "deprecationMessage": "Legacy feature will be removed in the future. Please check https://containers.dev/features for replacements. E.g., `ghcr.io/devcontainers/features/java` has an option to install Maven."
30 | },
31 | "gradle": {
32 | "deprecated": true,
33 | "deprecationMessage": "Legacy feature will be removed in the future. Please check https://containers.dev/features for replacements. E.g., `ghcr.io/devcontainers/features/java` has an option to install Gradle."
34 | },
35 | "homebrew": {
36 | "deprecated": true,
37 | "deprecationMessage": "Legacy feature not supported. Please check https://containers.dev/features for replacements."
38 | },
39 | "jupyterlab": {
40 | "deprecated": true,
41 | "deprecationMessage": "Legacy feature will be removed in the future. Please check https://containers.dev/features for replacements. E.g., `ghcr.io/devcontainers/features/python` has an option to install JupyterLab."
42 | }
43 | },
44 | "additionalProperties": true
45 | },
46 | "overrideFeatureInstallOrder": {
47 | "type": "array",
48 | "description": "Array consisting of the Feature id (without the semantic version) of Features in the order the user wants them to be installed.",
49 | "items": {
50 | "type": "string"
51 | }
52 | },
53 | "secrets": {
54 | "type": "object",
55 | "description": "Recommended secrets for this dev container. Recommendations are provided as environment variable keys with optional metadata.",
56 | "patternProperties": {
57 | "^[a-zA-Z_][a-zA-Z0-9_]*$": {
58 | "type": "object",
59 | "description": "Environment variable keys following unix-style naming conventions. eg: ^[a-zA-Z_][a-zA-Z0-9_]*$",
60 | "properties": {
61 | "description": {
62 | "type": "string",
63 | "description": "A description of the secret."
64 | },
65 | "documentationUrl": {
66 | "type": "string",
67 | "format": "uri",
68 | "description": "A URL to documentation about the secret."
69 | }
70 | },
71 | "additionalProperties": false
72 | },
73 | "additionalProperties": false
74 | },
75 | "additionalProperties": false
76 | },
77 | "forwardPorts": {
78 | "type": "array",
79 | "description": "Ports that are forwarded from the container to the local machine. Can be an integer port number, or a string of the format \"host:port_number\".",
80 | "items": {
81 | "oneOf": [
82 | {
83 | "type": "integer",
84 | "maximum": 65535,
85 | "minimum": 0
86 | },
87 | {
88 | "type": "string",
89 | "pattern": "^([a-z0-9-]+):(\\d{1,5})$"
90 | }
91 | ]
92 | }
93 | },
94 | "portsAttributes": {
95 | "type": "object",
96 | "patternProperties": {
97 | "(^\\d+(-\\d+)?$)|(.+)": {
98 | "type": "object",
99 | "description": "A port, range of ports (ex. \"40000-55000\"), or regular expression (ex. \".+\\\\/server.js\"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression.",
100 | "properties": {
101 | "onAutoForward": {
102 | "type": "string",
103 | "enum": [
104 | "notify",
105 | "openBrowser",
106 | "openBrowserOnce",
107 | "openPreview",
108 | "silent",
109 | "ignore"
110 | ],
111 | "enumDescriptions": [
112 | "Shows a notification when a port is automatically forwarded.",
113 | "Opens the browser when the port is automatically forwarded. Depending on your settings, this could open an embedded browser.",
114 | "Opens the browser when the port is automatically forwarded, but only the first time the port is forward during a session. Depending on your settings, this could open an embedded browser.",
115 | "Opens a preview in the same window when the port is automatically forwarded.",
116 | "Shows no notification and takes no action when this port is automatically forwarded.",
117 | "This port will not be automatically forwarded."
118 | ],
119 | "description": "Defines the action that occurs when the port is discovered for automatic forwarding",
120 | "default": "notify"
121 | },
122 | "elevateIfNeeded": {
123 | "type": "boolean",
124 | "description": "Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port.",
125 | "default": false
126 | },
127 | "label": {
128 | "type": "string",
129 | "description": "Label that will be shown in the UI for this port.",
130 | "default": "Application"
131 | },
132 | "requireLocalPort": {
133 | "type": "boolean",
134 | "markdownDescription": "When true, a modal dialog will show if the chosen local port isn't used for forwarding.",
135 | "default": false
136 | },
137 | "protocol": {
138 | "type": "string",
139 | "enum": [
140 | "http",
141 | "https"
142 | ],
143 | "description": "The protocol to use when forwarding this port."
144 | }
145 | },
146 | "default": {
147 | "label": "Application",
148 | "onAutoForward": "notify"
149 | }
150 | }
151 | },
152 | "markdownDescription": "Set default properties that are applied when a specific port number is forwarded. For example:\n\n```\n\"3000\": {\n \"label\": \"Application\"\n},\n\"40000-55000\": {\n \"onAutoForward\": \"ignore\"\n},\n\".+\\\\/server.js\": {\n \"onAutoForward\": \"openPreview\"\n}\n```",
153 | "defaultSnippets": [
154 | {
155 | "body": {
156 | "${1:3000}": {
157 | "label": "${2:Application}",
158 | "onAutoForward": "notify"
159 | }
160 | }
161 | }
162 | ],
163 | "additionalProperties": false
164 | },
165 | "otherPortsAttributes": {
166 | "type": "object",
167 | "properties": {
168 | "onAutoForward": {
169 | "type": "string",
170 | "enum": [
171 | "notify",
172 | "openBrowser",
173 | "openPreview",
174 | "silent",
175 | "ignore"
176 | ],
177 | "enumDescriptions": [
178 | "Shows a notification when a port is automatically forwarded.",
179 | "Opens the browser when the port is automatically forwarded. Depending on your settings, this could open an embedded browser.",
180 | "Opens a preview in the same window when the port is automatically forwarded.",
181 | "Shows no notification and takes no action when this port is automatically forwarded.",
182 | "This port will not be automatically forwarded."
183 | ],
184 | "description": "Defines the action that occurs when the port is discovered for automatic forwarding",
185 | "default": "notify"
186 | },
187 | "elevateIfNeeded": {
188 | "type": "boolean",
189 | "description": "Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port.",
190 | "default": false
191 | },
192 | "label": {
193 | "type": "string",
194 | "description": "Label that will be shown in the UI for this port.",
195 | "default": "Application"
196 | },
197 | "requireLocalPort": {
198 | "type": "boolean",
199 | "markdownDescription": "When true, a modal dialog will show if the chosen local port isn't used for forwarding.",
200 | "default": false
201 | },
202 | "protocol": {
203 | "type": "string",
204 | "enum": [
205 | "http",
206 | "https"
207 | ],
208 | "description": "The protocol to use when forwarding this port."
209 | }
210 | },
211 | "defaultSnippets": [
212 | {
213 | "body": {
214 | "onAutoForward": "ignore"
215 | }
216 | }
217 | ],
218 | "markdownDescription": "Set default properties that are applied to all ports that don't get properties from the setting `remote.portsAttributes`. For example:\n\n```\n{\n \"onAutoForward\": \"ignore\"\n}\n```",
219 | "additionalProperties": false
220 | },
221 | "updateRemoteUserUID": {
222 | "type": "boolean",
223 | "description": "Controls whether on Linux the container's user should be updated with the local user's UID and GID. On by default when opening from a local folder."
224 | },
225 | "containerEnv": {
226 | "type": "object",
227 | "additionalProperties": {
228 | "type": "string"
229 | },
230 | "description": "Container environment variables."
231 | },
232 | "containerUser": {
233 | "type": "string",
234 | "description": "The user the container will be started with. The default is the user on the Docker image."
235 | },
236 | "mounts": {
237 | "type": "array",
238 | "description": "Mount points to set up when creating the container. See Docker's documentation for the --mount option for the supported syntax.",
239 | "items": {
240 | "anyOf": [
241 | {
242 | "$ref": "#/definitions/Mount"
243 | },
244 | {
245 | "type": "string"
246 | }
247 | ]
248 | }
249 | },
250 | "init": {
251 | "type": "boolean",
252 | "description": "Passes the --init flag when creating the dev container."
253 | },
254 | "privileged": {
255 | "type": "boolean",
256 | "description": "Passes the --privileged flag when creating the dev container."
257 | },
258 | "capAdd": {
259 | "type": "array",
260 | "description": "Passes docker capabilities to include when creating the dev container.",
261 | "examples": [
262 | "SYS_PTRACE"
263 | ],
264 | "items": {
265 | "type": "string"
266 | }
267 | },
268 | "securityOpt": {
269 | "type": "array",
270 | "description": "Passes docker security options to include when creating the dev container.",
271 | "examples": [
272 | "seccomp=unconfined"
273 | ],
274 | "items": {
275 | "type": "string"
276 | }
277 | },
278 | "remoteEnv": {
279 | "type": "object",
280 | "additionalProperties": {
281 | "type": [
282 | "string",
283 | "null"
284 | ]
285 | },
286 | "description": "Remote environment variables to set for processes spawned in the container including lifecycle scripts and any remote editor/IDE server process."
287 | },
288 | "remoteUser": {
289 | "type": "string",
290 | "description": "The username to use for spawning processes in the container including lifecycle scripts and any remote editor/IDE server process. The default is the same user as the container."
291 | },
292 | "initializeCommand": {
293 | "type": [
294 | "string",
295 | "array",
296 | "object"
297 | ],
298 | "description": "A command to run locally (i.e Your host machine, cloud VM) before anything else. This command is run before \"onCreateCommand\". If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell. If this is an object, each provided command will be run in parallel.",
299 | "items": {
300 | "type": "string"
301 | },
302 | "additionalProperties": {
303 | "type": [
304 | "string",
305 | "array"
306 | ],
307 | "items": {
308 | "type": "string"
309 | }
310 | }
311 | },
312 | "onCreateCommand": {
313 | "type": [
314 | "string",
315 | "array",
316 | "object"
317 | ],
318 | "description": "A command to run when creating the container. This command is run after \"initializeCommand\" and before \"updateContentCommand\". If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell. If this is an object, each provided command will be run in parallel.",
319 | "items": {
320 | "type": "string"
321 | },
322 | "additionalProperties": {
323 | "type": [
324 | "string",
325 | "array"
326 | ],
327 | "items": {
328 | "type": "string"
329 | }
330 | }
331 | },
332 | "updateContentCommand": {
333 | "type": [
334 | "string",
335 | "array",
336 | "object"
337 | ],
338 | "description": "A command to run when creating the container and rerun when the workspace content was updated while creating the container. This command is run after \"onCreateCommand\" and before \"postCreateCommand\". If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell. If this is an object, each provided command will be run in parallel.",
339 | "items": {
340 | "type": "string"
341 | },
342 | "additionalProperties": {
343 | "type": [
344 | "string",
345 | "array"
346 | ],
347 | "items": {
348 | "type": "string"
349 | }
350 | }
351 | },
352 | "postCreateCommand": {
353 | "type": [
354 | "string",
355 | "array",
356 | "object"
357 | ],
358 | "description": "A command to run after creating the container. This command is run after \"updateContentCommand\" and before \"postStartCommand\". If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell. If this is an object, each provided command will be run in parallel.",
359 | "items": {
360 | "type": "string"
361 | },
362 | "additionalProperties": {
363 | "type": [
364 | "string",
365 | "array"
366 | ],
367 | "items": {
368 | "type": "string"
369 | }
370 | }
371 | },
372 | "postStartCommand": {
373 | "type": [
374 | "string",
375 | "array",
376 | "object"
377 | ],
378 | "description": "A command to run after starting the container. This command is run after \"postCreateCommand\" and before \"postAttachCommand\". If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell. If this is an object, each provided command will be run in parallel.",
379 | "items": {
380 | "type": "string"
381 | },
382 | "additionalProperties": {
383 | "type": [
384 | "string",
385 | "array"
386 | ],
387 | "items": {
388 | "type": "string"
389 | }
390 | }
391 | },
392 | "postAttachCommand": {
393 | "type": [
394 | "string",
395 | "array",
396 | "object"
397 | ],
398 | "description": "A command to run when attaching to the container. This command is run after \"postStartCommand\". If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell. If this is an object, each provided command will be run in parallel.",
399 | "items": {
400 | "type": "string"
401 | },
402 | "additionalProperties": {
403 | "type": [
404 | "string",
405 | "array"
406 | ],
407 | "items": {
408 | "type": "string"
409 | }
410 | }
411 | },
412 | "waitFor": {
413 | "type": "string",
414 | "enum": [
415 | "initializeCommand",
416 | "onCreateCommand",
417 | "updateContentCommand",
418 | "postCreateCommand",
419 | "postStartCommand"
420 | ],
421 | "description": "The user command to wait for before continuing execution in the background while the UI is starting up. The default is \"updateContentCommand\"."
422 | },
423 | "userEnvProbe": {
424 | "type": "string",
425 | "enum": [
426 | "none",
427 | "loginShell",
428 | "loginInteractiveShell",
429 | "interactiveShell"
430 | ],
431 | "description": "User environment probe to run. The default is \"loginInteractiveShell\"."
432 | },
433 | "hostRequirements": {
434 | "type": "object",
435 | "description": "Host hardware requirements.",
436 | "properties": {
437 | "cpus": {
438 | "type": "integer",
439 | "minimum": 1,
440 | "description": "Number of required CPUs."
441 | },
442 | "memory": {
443 | "type": "string",
444 | "pattern": "^\\d+([tgmk]b)?$",
445 | "description": "Amount of required RAM in bytes. Supports units tb, gb, mb and kb."
446 | },
447 | "storage": {
448 | "type": "string",
449 | "pattern": "^\\d+([tgmk]b)?$",
450 | "description": "Amount of required disk space in bytes. Supports units tb, gb, mb and kb."
451 | },
452 | "gpu": {
453 | "oneOf": [
454 | {
455 | "type": [
456 | "boolean",
457 | "string"
458 | ],
459 | "enum": [
460 | true,
461 | false,
462 | "optional"
463 | ],
464 | "description": "Indicates whether a GPU is required. The string \"optional\" indicates that a GPU is optional. An object value can be used to configure more detailed requirements."
465 | },
466 | {
467 | "type": "object",
468 | "properties": {
469 | "cores": {
470 | "type": "integer",
471 | "minimum": 1,
472 | "description": "Number of required cores."
473 | },
474 | "memory": {
475 | "type": "string",
476 | "pattern": "^\\d+([tgmk]b)?$",
477 | "description": "Amount of required RAM in bytes. Supports units tb, gb, mb and kb."
478 | }
479 | },
480 | "description": "Indicates whether a GPU is required. The string \"optional\" indicates that a GPU is optional. An object value can be used to configure more detailed requirements.",
481 | "additionalProperties": false
482 | }
483 | ]
484 | }
485 | },
486 | "unevaluatedProperties": false
487 | },
488 | "customizations": {
489 | "type": "object",
490 | "description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations."
491 | },
492 | "additionalProperties": {
493 | "type": "object",
494 | "additionalProperties": true
495 | }
496 | }
497 | },
498 | "nonComposeBase": {
499 | "type": "object",
500 | "properties": {
501 | "appPort": {
502 | "type": [
503 | "integer",
504 | "string",
505 | "array"
506 | ],
507 | "description": "Application ports that are exposed by the container. This can be a single port or an array of ports. Each port can be a number or a string. A number is mapped to the same port on the host. A string is passed to Docker unchanged and can be used to map ports differently, e.g. \"8000:8010\".",
508 | "items": {
509 | "type": [
510 | "integer",
511 | "string"
512 | ]
513 | }
514 | },
515 | "runArgs": {
516 | "type": "array",
517 | "description": "The arguments required when starting in the container.",
518 | "items": {
519 | "type": "string"
520 | }
521 | },
522 | "shutdownAction": {
523 | "type": "string",
524 | "enum": [
525 | "none",
526 | "stopContainer"
527 | ],
528 | "description": "Action to take when the user disconnects from the container in their editor. The default is to stop the container."
529 | },
530 | "overrideCommand": {
531 | "type": "boolean",
532 | "description": "Whether to overwrite the command specified in the image. The default is true."
533 | },
534 | "workspaceFolder": {
535 | "type": "string",
536 | "description": "The path of the workspace folder inside the container."
537 | },
538 | "workspaceMount": {
539 | "type": "string",
540 | "description": "The --mount parameter for docker run. The default is to mount the project folder at /workspaces/$project."
541 | }
542 | }
543 | },
544 | "dockerfileContainer": {
545 | "oneOf": [
546 | {
547 | "type": "object",
548 | "properties": {
549 | "build": {
550 | "type": "object",
551 | "description": "Docker build-related options.",
552 | "allOf": [
553 | {
554 | "type": "object",
555 | "properties": {
556 | "dockerfile": {
557 | "type": "string",
558 | "description": "The location of the Dockerfile that defines the contents of the container. The path is relative to the folder containing the `devcontainer.json` file."
559 | },
560 | "context": {
561 | "type": "string",
562 | "description": "The location of the context folder for building the Docker image. The path is relative to the folder containing the `devcontainer.json` file."
563 | }
564 | },
565 | "required": [
566 | "dockerfile"
567 | ]
568 | },
569 | {
570 | "$ref": "#/definitions/buildOptions"
571 | }
572 | ],
573 | "unevaluatedProperties": false
574 | }
575 | },
576 | "required": [
577 | "build"
578 | ]
579 | },
580 | {
581 | "allOf": [
582 | {
583 | "type": "object",
584 | "properties": {
585 | "dockerFile": {
586 | "type": "string",
587 | "description": "The location of the Dockerfile that defines the contents of the container. The path is relative to the folder containing the `devcontainer.json` file."
588 | },
589 | "context": {
590 | "type": "string",
591 | "description": "The location of the context folder for building the Docker image. The path is relative to the folder containing the `devcontainer.json` file."
592 | }
593 | },
594 | "required": [
595 | "dockerFile"
596 | ]
597 | },
598 | {
599 | "type": "object",
600 | "properties": {
601 | "build": {
602 | "description": "Docker build-related options.",
603 | "$ref": "#/definitions/buildOptions"
604 | }
605 | }
606 | }
607 | ]
608 | }
609 | ]
610 | },
611 | "buildOptions": {
612 | "type": "object",
613 | "properties": {
614 | "target": {
615 | "type": "string",
616 | "description": "Target stage in a multi-stage build."
617 | },
618 | "args": {
619 | "type": "object",
620 | "additionalProperties": {
621 | "type": [
622 | "string"
623 | ]
624 | },
625 | "description": "Build arguments."
626 | },
627 | "cacheFrom": {
628 | "type": [
629 | "string",
630 | "array"
631 | ],
632 | "description": "The image to consider as a cache. Use an array to specify multiple images.",
633 | "items": {
634 | "type": "string"
635 | }
636 | },
637 | "options": {
638 | "type": "array",
639 | "description": "Additional arguments passed to the build command.",
640 | "items": {
641 | "type": "string"
642 | }
643 | }
644 | }
645 | },
646 | "imageContainer": {
647 | "type": "object",
648 | "properties": {
649 | "image": {
650 | "type": "string",
651 | "description": "The docker image that will be used to create the container."
652 | }
653 | },
654 | "required": [
655 | "image"
656 | ]
657 | },
658 | "composeContainer": {
659 | "type": "object",
660 | "properties": {
661 | "dockerComposeFile": {
662 | "type": [
663 | "string",
664 | "array"
665 | ],
666 | "description": "The name of the docker-compose file(s) used to start the services.",
667 | "items": {
668 | "type": "string"
669 | }
670 | },
671 | "service": {
672 | "type": "string",
673 | "description": "The service you want to work on. This is considered the primary container for your dev environment which your editor will connect to."
674 | },
675 | "runServices": {
676 | "type": "array",
677 | "description": "An array of services that should be started and stopped.",
678 | "items": {
679 | "type": "string"
680 | }
681 | },
682 | "workspaceFolder": {
683 | "type": "string",
684 | "description": "The path of the workspace folder inside the container. This is typically the target path of a volume mount in the docker-compose.yml."
685 | },
686 | "shutdownAction": {
687 | "type": "string",
688 | "enum": [
689 | "none",
690 | "stopCompose"
691 | ],
692 | "description": "Action to take when the user disconnects from the primary container in their editor. The default is to stop all of the compose containers."
693 | },
694 | "overrideCommand": {
695 | "type": "boolean",
696 | "description": "Whether to overwrite the command specified in the image. The default is false."
697 | }
698 | },
699 | "required": [
700 | "dockerComposeFile",
701 | "service",
702 | "workspaceFolder"
703 | ]
704 | },
705 | "Mount": {
706 | "type": "object",
707 | "properties": {
708 | "type": {
709 | "type": "string",
710 | "enum": [
711 | "bind",
712 | "volume"
713 | ],
714 | "description": "Mount type."
715 | },
716 | "source": {
717 | "type": "string",
718 | "description": "Mount source."
719 | },
720 | "target": {
721 | "type": "string",
722 | "description": "Mount target."
723 | }
724 | },
725 | "required": [
726 | "type",
727 | "target"
728 | ],
729 | "additionalProperties": false
730 | }
731 | },
732 | "oneOf": [
733 | {
734 | "allOf": [
735 | {
736 | "oneOf": [
737 | {
738 | "allOf": [
739 | {
740 | "oneOf": [
741 | {
742 | "$ref": "#/definitions/dockerfileContainer"
743 | },
744 | {
745 | "$ref": "#/definitions/imageContainer"
746 | }
747 | ]
748 | },
749 | {
750 | "$ref": "#/definitions/nonComposeBase"
751 | }
752 | ]
753 | },
754 | {
755 | "$ref": "#/definitions/composeContainer"
756 | }
757 | ]
758 | },
759 | {
760 | "$ref": "#/definitions/devContainerCommon"
761 | }
762 | ]
763 | },
764 | {
765 | "type": "object",
766 | "$ref": "#/definitions/devContainerCommon",
767 | "additionalProperties": false
768 | }
769 | ],
770 | "unevaluatedProperties": false
771 | }
772 |
--------------------------------------------------------------------------------