Help us shape the future of Go! Take the 2026 Go Developer Survey

features

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 25, 2026 License: MIT Imports: 26 Imported by: 0

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

View Source
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

func BuiltinEnvContent(containerUser, remoteUser string) string

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

func BuiltinHomesCommand(path, containerUser, remoteUser string) string

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

func ContainerUsers(containerUser, remoteUser, imageUser string) (string, string)

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

func InstallCommand(dir string) string

InstallCommand returns the shell command that runs the install wrapper of a feature staged at dir.

func LockfilePath

func LockfilePath(configPath string) string

func QuoteContainerEnv

func QuoteContainerEnv(v string) string

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.

func SafeEnvID

func SafeEnvID(s string) string

SafeEnvID converts a feature option name or id to an environment variable name like upstream's getSafeId: non-word characters become "_", a leading run of digits and underscores becomes a single "_", and the result is upper-cased.

Types

type LockedFeature

type LockedFeature struct {
	Version   string   `json:"version"`
	Resolved  string   `json:"resolved"`
	Integrity string   `json:"integrity"`
	DependsOn []string `json:"dependsOn,omitempty"`
}

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 GenerateLockfile(resolved []Resolved) *Lockfile

func (Lockfile) MarshalJSON

func (l Lockfile) MarshalJSON() ([]byte, error)

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

func (m Metadata) ContainerEnvKeys() []string

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

func (r Resolved) EnvFileContent() string

EnvFileContent returns the devcontainer-features.env content: one NAME="value" line per option, with names from SafeEnvID.

func (Resolved) InstallWrapperContent

func (r Resolved) InstallWrapperContent() string

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

func (r Resolved) OptionValues() map[string]any

OptionValues returns the feature's effective option values: the declared defaults overlaid with the configured options, like upstream's getFeatureValueObject.

func (Resolved) SourceWithoutVersion

func (r Resolved) SourceWithoutVersion() string

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 New

func New() *Resolver

New constructs a Resolver with default configuration.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL