Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

Feature request: First-class array input for Feature options (array option type + multiple invocations) [blocked on spec#766]

オープン
#1,298 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
5/5
見積もり時間
1週間以上
初心者へのやさしさ
35/100
issue の種類
機能追加
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
typescript
領域
cli, devtools

調査の方向性

この実装は明示的に受け入れ待ちでブロックされているため、まず spec#766 RFC から始めてください。次に src/spec-configuration/containerFeaturesConfiguration.ts と configuration.ts を読み、その後 getFeatureLayers と getFeatureInstallWrapperScript を確認してください。スキーマの変更については devContainerFeature.schema.json を調べてください。完了とは、配列オプション、強制変換、シリアライズ、CLI フラグの繰り返し、複数回の呼び出し、バリデーション、および一覧にあるテスト計画のケースがすべて機能することです。

索引モデルが issue の本文から書いたものです。

説明

Feature request: First-class array input for Feature options (array option type + multiple invocations)

Complex feature request — depends on the spec change in
devcontainers/spec#766 (RFC).

This issue tracks the reference CLI implementation work once the spec RFC is accepted.
Related long-standing issues: spec#57
(array option type, open since 2022) and spec#44
(install a feature more than once).


Problem

The dev container spec restricts Feature option values to boolean and string. There is no
array type
. As a result, every Feature that needs a list (packages, extensions, tools, versions)
is forced to accept a comma-separated string and split it inside install.sh.

This CLI encodes that limitation directly in its TypeScript types, which is the root cause that
blocks any progress:

// src/spec-configuration/containerFeaturesConfiguration.ts
export type FeatureOption = {
    type: 'boolean';
    default?: boolean;
    description?: string;
} | {
    type: 'string';
    enum?: string[];
    default?: string;
    description?: string;
} | {
    type: 'string';
    proposals?: string[];
    default?: string;
    description?: string;
};
// src/spec-configuration/configuration.ts
export interface DevContainerFeature {
    userFeatureId: string;
    options: boolean | string | Record<string, boolean | string | undefined>;
}
// ...
features?: Record<string, string | boolean | Record<string, string | boolean>>;

FeatureOption has no 'array' variant. DevContainerFeature.options and the features map
only permit boolean | string for option values — arrays are not representable, so they can
never reach install.sh no matter what a user writes in devcontainer.json.

Real-world impact (shipped Features, today)
Feature Option Today Symptom
ghcr.io/rocker-org/devcontainer-features/r-packages:1 packages "cli,rlang" Comma-joined; values containing commas are unrepresentable.
ghcr.io/devcontainers/features/github-cli extensions "github/gh-copilot" Comma-joined; extension refs/args with commas break.
mwmahlberg/devcontainer-features npm-packages packages "typescript,eslint" Accepts comma or whitespace or newline — three delimiters, because the spec gives no canonical list form.

The npm-packages case is the clearest failure signal: with no array type, every Feature author
invents a different delimiter
. Consumers cannot reason about a list option without reading each
Feature's install.sh.

This was always meant to be temporary. From spec#57,
maintainer @Chuxel (2022):

Right now things in devcontainers/features are using a comma separated string as a near term
workaround
. Converting this into an array is pretty easy…

That workaround has now been the de facto standard for 3+ years.


Requirements (hard — no alternatives)

This is a required capability, not a nice-to-have. Comma-separated strings are not an acceptable
long-term substitute (they are ambiguous, lossy, and force per-Feature delimiter conventions). The
CLI must implement:

  1. array option typedevcontainer-feature.json may declare "type": "array" for an
    option, with default as an array and proposals/enum constraining elements.
  2. Array option values in devcontainer.json"packages": ["curl","git","jq"] must be
    accepted, validated, and propagated.
  3. Multiple invocations via array of option objects — a Feature value may be an array of option
    objects ("dotnet": [{"version":"3.1"},{"version":"6.0"}]), invoking install.sh once per
    element in order (resolves spec#44).
  4. Option Resolution for arrays — array values are serialized to devcontainer-features.env as
    a JSON array string (PACKAGES='["curl","git","jq"]'), the only delimiter-free, unambiguous
    encoding.
  5. Backward-compatible string→array coercion — if a string is supplied for an array-typed
    option: parse as JSON if it looks like a JSON array, else split on commas. Existing
    comma-separated Features keep working when they migrate to type: "array".
  6. CLI flag support — accept JSON array values in --override-features, and support repeated
    flags (--feature-option <feature>.<option> <value>) that append to an array option.

Proposed implementation

1. Type changes

src/spec-configuration/containerFeaturesConfiguration.ts — extend FeatureOption:

export type FeatureOption = {
    type: 'boolean';
    default?: boolean;
    description?: string;
} | {
    type: 'string';
    enum?: string[];
    default?: string;
    description?: string;
} | {
    type: 'string';
    proposals?: string[];
    default?: string;
    description?: string;
} | {
    type: 'array';                          // NEW
    enum?: string[];                        // constrains elements
    proposals?: string[];                   // suggests elements
    default?: (string | boolean | number)[];// array default
    description?: string;
};

src/spec-configuration/configuration.ts — widen option value and Feature value types:

export interface DevContainerFeature {
    userFeatureId: string;
    // allow arrays of primitives as option values, and array-of-option-objects as the Feature value
    options: boolean | string | (string | boolean | number)[] | Record<string, boolean | string | (string | boolean | number)[] | undefined> | Record<string, boolean | string | (string | boolean | number)[]>[];
}
// ...
features?: Record<string, string | boolean | (string | boolean | number)[] | Record<string, boolean | string | (string | boolean | number)[]> | Record<string, boolean | string | (string | boolean | number)[]>[]>;
2. Option parsing & normalization

Where feature option values are read and normalized (the getFeatureValueDefaults /
option-resolution path in containerFeaturesConfiguration.ts):

  • When option.type === 'array':
    • Accept a JSON array value as-is.
    • If the supplied value is a string, attempt JSON.parse; if it yields an array, use it;
      otherwise split on , and trim each element. Emit a warning recommending the array form.
    • Validate each element against enum/proposals when present.
    • Default to [] when omitted and no default.
3. Option Resolution (env serialization)

In the code that writes devcontainer-features.env (<OPTION_NAME>=<value>):

  • For an array option, serialize the value with JSON.stringify(value) so the env var holds the
    canonical JSON array string. Example output:
    PACKAGES='["curl","git","jq"]'
    
  • This keeps a single, unambiguous source of truth. install.sh parses with jq (already
    ubiquitous in dev container images):
    for pkg in $(printf '%s' "$PACKAGES" | jq -r '.[]'); do apt-get install -y "$pkg"; done
    
4. Multiple invocations (array of option objects)

In the feature install layer (getFeatureLayers / getFeatureInstallWrapperScript and the
surrounding orchestration):

  • When a Feature's value is an array of option objects, emit one install layer/wrapper per element,
    in array order, each with its own devcontainer-features.env. All invocations of a given Feature
    run consecutively at that Feature's position in the install order (do not interleave with other
    Features).
5. CLI flags
  • --override-features: already accepts a JSON blob; ensure array option values and array-of-objects
    Feature values parse and flow through the new types.
  • Add/confirm a repeated-flag form: --feature-option <feature>.<option> <value> appends to an
    array-typed option (and sets/replaces for scalar options).
6. Validation & errors
  • Reject non-array values for array-typed options (after string coercion) with a clear error
    naming the Feature and option.
  • Validate enum elements; report the offending element.

Test plan

  • devcontainer-feature.json with type: "array" parses and surfaces default/enum/proposals.
  • devcontainer.json with "packages": ["curl","git"] reaches install.sh as
    PACKAGES='["curl","git"]'.
  • String "curl,git" for an array option coerces to ["curl","git"] with a warning.
  • String '["curl","git"]' (JSON) coerces to the array without warning.
  • enum-constrained array option rejects an out-of-set element.
  • Array-of-option-objects Feature value invokes install.sh twice with distinct env vars, in
    order.
  • Repeated --feature-option flags append to an array option.
  • Existing comma-separated Features (e.g. r-packages) continue to work unchanged after
    migrating their option to type: "array".
  • JSON Schema (devContainerFeature.schema.json) validates the new shapes.

Use cases

  1. Package listsr-packages, npm-packages, github-cli extensions, Homebrew formulae:
    explicit arrays, values may contain commas.
  2. Multiple runtime versions — install .NET 3.1 and 6.0 (or Node 18 + 20) in one image
    via array-of-option-objects, without bespoke per-Feature "multi-version" options.
  3. Multi-select tool bundles — a Feature offering a curated subset of tools; with enum
    elements the UX can render a multi-select picker.
  4. Tool-readable config — schema validation, IntelliSense, and diffs work natively on JSON
    arrays; comma-strings are opaque to every tool except the splitting install.sh.

Prior art (alternative stacks)

The dev container spec is the only major dev-environment format lacking a native list type for
user-supplied option values:

Stack List input Notes
Coder coder_parameter type = "list(string)"; UI multi-select/tag-select; defaults via jsonencode([...]) First-class list type. Coder's docs warn that overriding list(string) on the CLI is "tricky" (CSV+JSON quoting) and offer a YAML workaround — exactly the ambiguity this CLI should avoid by defining array semantics up front.
Nix (mkShell) Native lists packages = [ curl git jq ]; First-class; no string parsing.
Gitpod (.gitpod.yml) Native YAML arrays (tasks, ports, vscode.extensions) First-class.
Docker Compose Native YAML arrays (volumes, ports, environment) First-class.
Helm Native YAML arrays in values.yaml, iterated with range First-class.
Terraform list(string), list(any) native variable types First-class.
Dev Containers (this CLI) ❌ No array option type — comma-separated string only Outlier.

Dependencies & unblocking

  • Blocked on spec acceptance: devcontainers/spec#766
    (the RFC defining array option type, Option Resolution for arrays, and array-of-option-objects).
  • Closes the long-standing workaround: spec#57
    (2022) and spec#44.

Once the spec RFC lands, this issue is the implementation tracker for the reference CLI. The type
changes in §1 are the minimal unblocking step; everything else follows from the spec's normative
requirements.


References

主要言語
TypeScript
スター
3k
フォーク
461
平均マージ
18分
マージ済み PR(30日)
5

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

devcontainers/cli のほかの issue

devcontainers/cli の issue をすべて見る

似ている issue

TypeScript の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。