This has now been implemented:
- Discussion issue: #236
- CLI PR: devcontainers/cli#564
Below is the original proposal.
Introduce a lockfile that records the exact version, download information and checksums for each feature listed in the devcontainer.json.
This will allow for:
- Improved reproducibility of image builds (installing "latest" of a tool will still have different outcomes as the tool publishes new releases).
- Improved cachability of image builds (image cache checksums will remain stable when the lockfile pins a feature to a particular version).
- Improved security by detecting when a feature's release artifact changes after its checksum was first recorded in the lockfile ("trust on first use").
(The following is inspired by NPM's package-lock.json
and Yarn's yarn.lock
.)
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:
resolved
:- OCI feature: A qualified feature id with the sha256 hash (not the version number).
- tarball feature: The
https:
URL used to download the feature.
version
: The full version number.integrity
:sha256:
followed by the hex encoded SHA-256 checksum of the download artifact.dependsOn
: An array of feature identifiers equal to the feature'sdependsOn
keys in itsdevcontainer-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.
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.
Local features and the deprecated GitHub releases features are not recorded in the lockfile.
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.
Example:
{
"features": {
"ghcr.io/devcontainers/features/node:1": {
"version": "1.0.4",
"resolved": "ghcr.io/devcontainers/features/node@sha256:567d704b3f4d3eca3acee51ded7c460a8395436d135d53d1175fb565daff42b8",
"integrity": "sha256:567d704b3f4d3eca3acee51ded7c460a8395436d135d53d1175fb565daff42b8"
},
"https://mycustomdomain.com/devcontainer-feature-myfeature.tgz": {
"version": "1.2.3",
"resolved": "https://mycustomdomain.com/devcontainer-feature-myfeature.tgz",
"integrity": "sha256:567d704b3f4d3eca3acee51ded7c460a8395436d135d53d1175fb565daff42b8"
}
}
}
Example with dependency:
{
"features": {
"ghcr.io/codspace/dependson/a": {
"version": "1.2.1",
"resolved": "ghcr.io/codspace/dependson/a@sha256:932027ef71da186210e6ceb3294c3459caaf6b548d2b547d5d26be3fc4b2264a",
"integrity": "sha256:932027ef71da186210e6ceb3294c3459caaf6b548d2b547d5d26be3fc4b2264a",
"dependsOn": [
"ghcr.io/codspace/dependson/e:2"
]
},
"ghcr.io/codspace/dependson/e:2": {
"version": "2.3.4",
"resolved": "ghcr.io/codspace/dependson/e@sha256:9f36f159c70f8bebff57f341904b030733adb17ef12a5d58d4b3d89b2a6c7d5a",
"integrity": "sha256:9f36f159c70f8bebff57f341904b030733adb17ef12a5d58d4b3d89b2a6c7d5a"
}
}
}