Documentation
¶
Overview ¶
relationships:
references: [features, mapping, ../../reference/cli/src/spec-configuration/containerFeaturesConfiguration]
Package features resolves and stages devcontainer features so they can be copied into a build context and executed during image build.
A feature is a small artifact (typically published as an OCI image) that contributes an install script plus metadata describing dependencies, the installation order, and any options. See https://containers.dev/implementors/features/ for the spec.
This package implements just enough of the spec to:
- Resolve features from OCI references like ghcr.io/devcontainers/features/node:1.
- Resolve features from local filesystem paths (./local-feature) or git tarballs delivered as direct file paths.
- Resolve features from direct HTTPS archives.
- Extract them into a staging directory.
- Determine installation order, honoring `installsAfter` and `overrideFeatureInstallOrder`.
relationships:
references: [features, ../../reference/cli/src/spec-configuration/lockfile]
relationships:
references: [lockfile, ../../reference/cli/src/spec-configuration/lockfile]
relationships:
references: [features, ../../reference/cli/src/spec-configuration/containerFeaturesConfiguration]
relationships:
references: [features, lockfile, stage]
Index ¶
- Constants
- func BuiltinEnvContent(containerUser, remoteUser string) string
- func BuiltinHomesCommand(path, containerUser, remoteUser string) string
- func ContainerUsers(containerUser, remoteUser, imageUser string) (string, string)
- func InstallCommand(dir string) string
- func LockfilePath(configPath string) string
- func QuoteContainerEnv(v string) string
- func SafeEnvID(s string) string
- type LockedFeature
- type Lockfile
- type LockfileState
- type Metadata
- type Option
- type Resolved
- type Resolver
Constants ¶
const ( // InstallDir is the directory features are installed from. InstallDir = "/tmp/dev-container-features" // BuiltinEnvFile holds _CONTAINER_USER, _REMOTE_USER and their homes. BuiltinEnvFile = "devcontainer-features.builtin.env" // EnvFile holds a feature's option values. EnvFile = "devcontainer-features.env" // InstallWrapperFile is the script that runs a feature's install.sh. InstallWrapperFile = "devcontainer-features-install.sh" )
Install layout inside the image, matching upstream (reference/cli/src/spec-configuration/containerFeaturesConfiguration.ts):
/tmp/dev-container-features/devcontainer-features.builtin.env /tmp/dev-container-features/<feature>/devcontainer-features.env /tmp/dev-container-features/<feature>/devcontainer-features-install.sh /tmp/dev-container-features/<feature>/install.sh (and the rest of the feature)
The install wrapper exports both env files and runs install.sh from the feature directory.
Variables ¶
This section is empty.
Functions ¶
func BuiltinEnvContent ¶
BuiltinEnvContent returns the initial devcontainer-features.builtin.env content. The home directories are appended inside the image by BuiltinHomesCommand, since they depend on the image's /etc/passwd.
func BuiltinHomesCommand ¶
BuiltinHomesCommand returns a shell command that appends _CONTAINER_USER_HOME and _REMOTE_USER_HOME to the builtin env file at path, looked up from the image's passwd database.
func ContainerUsers ¶
ContainerUsers returns the container and remote users features install for, like upstream's findContainerUsers: the container user is the configured containerUser, else the image user, else root; the remote user is the configured remoteUser, else the container user.
func InstallCommand ¶
InstallCommand returns the shell command that runs the install wrapper of a feature staged at dir.
func LockfilePath ¶
func QuoteContainerEnv ¶
QuoteContainerEnv quotes a feature's containerEnv value as the reference CLI writes it into its Dockerfile ENV instruction: double quotes, with `"` and `\` escaped and `$` left for the Dockerfile to expand.
Types ¶
type LockedFeature ¶
type Lockfile ¶
type Lockfile struct {
Features map[string]LockedFeature `json:"features"`
}
Lockfile records remote features by their original user-supplied identifiers. Local features are omitted, as in upstream devcontainer-lock.json.
func GenerateLockfile ¶
func (Lockfile) MarshalJSON ¶
MarshalJSON keeps upstream's localeCompare order instead of Go's byte order. The CLI's default English collation sorts lowercase before uppercase.
type LockfileState ¶
type LockfileState struct {
Lockfile *Lockfile
// contains filtered or unexported fields
}
LockfileState holds one input snapshot and its completed resolution. Keep it for the entire command, then Publish only after all command work succeeds. A state belongs to one invocation; concurrent invocations use separate states.
func ReadLockfile ¶
func ReadLockfile(configPath string, create, frozen bool) (*LockfileState, error)
func (*LockfileState) Prepare ¶
func (s *LockfileState) Prepare(resolved []Resolved) error
Prepare validates frozen mode before building. The candidate is held in memory so an interrupted, failed or cancelled command cannot publish it.
func (*LockfileState) Publish ¶
func (s *LockfileState) Publish(ctx context.Context) error
Publish replaces the destination with a complete sibling file. Each writer owns its temporary file; readers see either complete generation. A frozen state never writes, even when creation was requested as well.
type Metadata ¶
type Metadata struct {
Customizations map[string]any `json:"customizations,omitempty"`
ID string `json:"id"`
Version string `json:"version"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
DocumentationURL string `json:"documentationURL,omitempty"`
Options map[string]Option `json:"options,omitempty"`
ContainerEnv map[string]string `json:"containerEnv,omitempty"`
Mounts []any `json:"mounts,omitempty"`
Init *bool `json:"init,omitempty"`
Privileged *bool `json:"privileged,omitempty"`
CapAdd []string `json:"capAdd,omitempty"`
SecurityOpt []string `json:"securityOpt,omitempty"`
Entrypoint string `json:"entrypoint,omitempty"`
InstallsAfter []string `json:"installsAfter,omitempty"`
CurrentID string `json:"currentId,omitempty"`
DependsOn spec.OrderedMap `json:"dependsOn,omitempty"`
LegacyIDs []string `json:"legacyIds,omitempty"`
Deprecated bool `json:"deprecated,omitempty"`
OnCreateCommand spec.Command `json:"onCreateCommand,omitempty"`
UpdateContent spec.Command `json:"updateContentCommand,omitempty"`
PostCreateCommand spec.Command `json:"postCreateCommand,omitempty"`
PostStartCommand spec.Command `json:"postStartCommand,omitempty"`
PostAttachCommand spec.Command `json:"postAttachCommand,omitempty"`
// contains filtered or unexported fields
}
Metadata is the parsed devcontainer-feature.json bundled inside each feature artifact.
func (Metadata) ContainerEnvKeys ¶
ContainerEnvKeys returns the keys of m.ContainerEnv in the order the reference CLI applies them: ECMAScript Object.keys order over the devcontainer-feature.json object. Keys that are array indices come first in ascending numeric order, then the other keys in declaration order. The reference emits one ENV instruction per key in this order, so a value can reference a key applied before it. A key with no recorded position, as in a Metadata built in code, follows the other keys in sorted order.
type Option ¶
type Option struct {
Type string `json:"type,omitempty"`
Default any `json:"default,omitempty"`
Description string `json:"description,omitempty"`
Enum []string `json:"enum,omitempty"`
Proposals []string `json:"proposals,omitempty"`
}
Option models a single configurable feature option.
type Resolved ¶
type Resolved struct {
// Source is the original spec key (e.g. ghcr.io/devcontainers/features/node:1).
Source string
// Reference is the parsed OCI reference, if Source was an OCI ref.
Reference *oci.Reference
// Manifest is the resolved OCI manifest used for configuration reporting.
Manifest *oci.Manifest
// Options as supplied in devcontainer.json (raw JSON values).
Options map[string]any
// Value is the original option value, including scalar shorthand.
Value any
// Metadata loaded from the feature's devcontainer-feature.json.
Metadata Metadata
// Dir is the on-disk directory containing the feature contents
// (devcontainer-feature.json, install.sh, etc.).
Dir string
// SafeID is a path-safe identifier used to namespace the feature
// inside the staging directory.
SafeID string
// Digest is the manifest digest of an OCI feature. Empty for local
// features.
Digest string
// Tarball is the direct HTTPS archive URL. Digest hashes its archive bytes.
Tarball string
// contains filtered or unexported fields
}
Resolved is a single feature ready for inclusion in a build.
func (Resolved) EnvFileContent ¶
EnvFileContent returns the devcontainer-features.env content: one NAME="value" line per option, with names from SafeEnvID.
func (Resolved) InstallWrapperContent ¶
InstallWrapperContent returns the devcontainer-features-install.sh script for the feature, like upstream's getFeatureInstallWrapperScript. It exports the builtin and option env files, then runs install.sh. The feature's entrypoint is never run at install time.
func (Resolved) OptionValues ¶
OptionValues returns the feature's effective option values: the declared defaults overlaid with the configured options, like upstream's getFeatureValueObject.
func (Resolved) SourceWithoutVersion ¶
SourceWithoutVersion returns the feature source without its tag or digest.
type Resolver ¶
type Resolver struct {
// SkipAutoMapping leaves legacy shorthand identifiers unchanged.
SkipAutoMapping bool
// CacheDir, if non-empty, is used to cache pulled OCI artifacts
// keyed by digest. Otherwise resolution always re-fetches.
CacheDir string
// OCI is the registry client used for OCI-based features.
OCI *oci.Client
// Logger receives progress messages. May be nil.
Logger func(format string, args ...any)
}
Resolver pulls and stages features.
func (*Resolver) Resolve ¶
func (r *Resolver) Resolve(specFeatures spec.OrderedMap, override []string, stagingDir, localBaseDir string) ([]Resolved, error)
Resolve fetches all features from the given spec into stagingDir. Local feature references are resolved relative to localBaseDir when they are not absolute. The returned slice is in install order.
func (*Resolver) ResolveWithLockfile ¶
func (r *Resolver) ResolveWithLockfile(ctx context.Context, specFeatures spec.OrderedMap, override []string, stagingDir, localBaseDir string, lock *Lockfile) ([]Resolved, error)
ResolveWithLockfile uses the supplied immutable snapshot for root features and transitive dependencies. A lock entry's integrity pins the original repository; its resolved URL is descriptive, never a redirect authority.