Skip to content

Add first-class superposition.yml project ports with generation-time expansion#162

Open
Copilot wants to merge 12 commits into
mainfrom
copilot/add-custom-ports-support
Open

Add first-class superposition.yml project ports with generation-time expansion#162
Copilot wants to merge 12 commits into
mainfrom
copilot/add-custom-ports-support

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Jun 3, 2026

This change adds a project-level ports field to superposition.yml so teams can declare explicit host/container bindings (with ${VAR} / ${VAR:-default} expansion) that work across both plain and compose stacks. These project-defined ports are materialized at generation time and intentionally bypass portOffset.

  • Project config model (superposition.yml)

    • Added ports support in parser/types/serialization (string shorthand and object form).
    • Added optional per-port metadata: label and onAutoForward.
    • Added schema enum constraints for onAutoForward allowed values.
  • Generation behavior

    • Added generation-time resolution from repo-root .env for ${VAR} / ${VAR:-default}.
    • plain: injects resolved host ports into devcontainer.json.forwardPorts and metadata into portsAttributes.
    • compose: also injects resolved bindings into docker-compose.yml services.devcontainer.ports.
    • Project ports are applied after offset handling so they are not shifted by portOffset.
  • Schema and docs

    • Regenerated tool/schema/superposition.schema.json to include ports.
    • Updated docs/superposition-yml.md and README authoring examples with ports usage and metadata.
    • Updated CHANGELOG with the new feature and semantics.
ports:
  - ${API_PORT:-8080}:8080
  - value: ${WEB_DEV_PORT:-5173}:5173
    label: Web dev server
    onAutoForward: openBrowser

@veggerby veggerby marked this pull request as ready for review June 3, 2026 07:09
Copilot AI review requested due to automatic review settings June 3, 2026 07:09
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds first-class project port bindings to superposition.yml, including generation-time ${VAR} / ${VAR:-default} expansion from the repo-root .env, and applies these ports to both plain and compose outputs.

Changes:

  • Extends the project config model + schema to support ports entries (string shorthand and object form with label / onAutoForward)
  • Resolves project ports at generation time and applies them to devcontainer.json (and to docker-compose.yml for compose stacks), explicitly bypassing portOffset
  • Adds Vitest coverage and updates docs/README/CHANGELOG to describe the new ports feature and ordering

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tool/schema/types.ts Adds ProjectPort and ProjectPortAutoForwardAction types and wires projectPorts / ports into selection/answers models
tool/schema/superposition.schema.json Regenerated schema including the new ports field and its allowed onAutoForward values
tool/schema/project-config.ts Parses/serializes ports from superposition.yml and maps selection ↔ answers (portsprojectPorts)
tool/questionnaire/composer.ts Implements generation-time port resolution from root .env and applies results to devcontainer + compose outputs
tool/tests/project-ports.test.ts Adds tests for parsing/serialization and for generation-time application in plain/compose stacks
scripts/generate-schema.ts Updates schema generator to emit the ports schema block and enum values
README.md Adds a minimal ports: example to the project config snippet
docs/superposition-yml.md Documents the new ports field, supported metadata, and application order
CHANGELOG.md Adds an [Unreleased] entry documenting the new ports capability and its portOffset bypass semantics

Comment on lines +1265 to +1266
config.forwardPorts = existingForwardPorts as number[];

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in the latest commit. Plain-stack HOST:CONTAINER bindings are now supported: both sides are resolved and validated, the container port goes into forwardPorts, and when hostPort !== containerPort a -p hostPort:containerPort entry is appended to runArgs. When hostPort === containerPort no runArgs entry is added (no-op case).

Comment thread tool/__tests__/project-ports.test.ts Outdated
Comment on lines +99 to +102
) as {
forwardPorts?: number[];
portsAttributes?: Record<string, { label?: string; onAutoForward?: string }>;
};
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in the same commit. The "throws for HOST:CONTAINER on plain stack" test is replaced with two tests that verify the concrete binding: one asserts forwardPorts contains the container port (8080) and runArgs contains "-p" / "9001:8080" when host ≠ container, and another asserts no -p entry is added when host == container.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 3, 2026

📦 Prerelease published to npm

A prerelease version has been published for this PR:

npm install container-superposition@0.1.12-pr.162.26975808453

Or run (regen) directly from the prerelease version:

npx container-superposition@0.1.12-pr.162.26975808453 regen

veggerby added 7 commits June 3, 2026 15:26
- Updated documentation for `ports` section to clarify usage for `stack: plain` and `stack: compose`.
- Enhanced test coverage for project port serialization and resolution, ensuring correct handling of both string and object entries.
- Implemented new logic in `prepareProjectPorts` to differentiate between plain and compose stacks, enforcing correct port binding formats.
- Improved error handling for unresolved environment variables and invalid port configurations.
- Adjusted type definitions for project ports to accommodate new structure and validation rules.
- Updated documentation to clarify the use of `${VAR}` and `{{cs.KEY}}` in `env:` values, including their resolution times and safety for secrets.
- Implemented checks for sensitive parameters in project files and `.devcontainer/.env`, warning users about hardcoded sensitive values.
- Added unit tests for parameter token substitution and validation, ensuring proper handling of `{{cs.KEY}}` and `${VAR}` expressions.
- Introduced a utility for parsing simple env files, improving code reuse across commands.
- Enhanced schema definitions to reflect new features and provide better descriptions for env variable values.
- Added support for project-only parameters in `superposition.yml`, allowing users to define parameters not declared by any overlay.
- Updated documentation to reflect the new project-only parameters feature, including examples of usage and console output.
- Enhanced the `doctor` command to warn about project-only parameters, providing guidance on their usage.
- Modified parameter resolution logic to include project-only parameters in the output and ensure they are available for substitution in environment values.
- Updated tests to cover the new functionality, ensuring correct handling and output of project-only parameters.
Copilot AI requested a review from veggerby June 4, 2026 07:46
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 31 out of 32 changed files in this pull request and generated 11 comments.

Comment thread docs/superposition-yml.md
Comment on lines +256 to +258
Write a container port number or `${VAR:-default}` expression. Do **not** use `HOST:CONTAINER`
format — the tool rejects it with an error. The tool **resolves** `${VAR}` at generation time
using `superposition.yml env` first, then the root `.env`, then the inline default.
Comment thread tool/schema/types.ts
Comment on lines +154 to +158
* For stack: plain — Container port expression, e.g. "${API_PORT:-8080}" or "8080".
* Must NOT contain ":". Resolved from superposition.yml env,
* then root .env, then inline default.
* For stack: compose — Full docker-compose short syntax, e.g. "${API_PORT:-8080}:8080".
* Must contain ":". Written VERBATIM to docker-compose ports;
Comment on lines +282 to +286
ports: {
type: 'array',
description:
'Project port bindings expanded at generation time (supports ${VAR} and ${VAR:-default}) and applied to both devcontainer.json and compose output. These ports are not affected by portOffset.',
items: {
Comment on lines +303 to +307
label: {
type: 'string',
description:
'Optional devcontainer.json portsAttributes label for the resolved host port',
},
Comment on lines +2288 to +2292
merged.services.devcontainer.ports = [
...new Set([
...existing,
...resolvedProjectPorts.map((port) => port.rawBinding ?? port.value),
]),
Comment thread tool/commands/doctor.ts
Comment on lines +1255 to +1257
'Move port bindings to the top-level ports: field.',
'ports: supports validation, auto-forward, and port-offset. See spec 024.',
],
Comment thread CHANGELOG.md
Comment on lines +19 to +22
- **Console output for unlisted parameters** — the `⚠️ Unknown overlay parameters …` warning
(yellow, comma-separated) is replaced by a neutral `⚙️ Project-only parameters (not declared
by any selected overlay):` informational block using the same `KEY=VALUE` per-line format as
overlay parameters.
Comment thread CHANGELOG.md
Comment on lines +86 to +89
- **`ports` semantics corrected** — **BREAKING** for any `superposition.yml` that uses `ports` on `stack: plain`
- Plain stack: `value` must now be a bare container port expression (no colon). Any `HOST:CONTAINER` format on plain stack now throws an error at generation time with a clear message.
- Compose stack: `value` is now written **verbatim** to `docker-compose.yml`; `${VAR}` references are no longer expanded by the tool. `portsAttributes` key is now the extracted container port, not the host port.
- **Migration**: change `"8080:8080"` → `"8080"` and `"${API_PORT:-8080}:8080"` → `"${API_PORT:-8080}"` for plain-stack ports.
Comment thread .pi/settings.json
Comment on lines 1 to 4
{
"enableSkillCommands": true,
"enableInstallTelemetry": false,
"extensions": ["extensions/subagent"]
"enableInstallTelemetry": false
}
Comment on lines +24 to +26
- Write: a container port number or `${VAR:-default}` expression.
- Do **not** write `HOST:CONTAINER` — the tool rejects it with an error.
- Use `env` in `superposition.yml` to drive the port value. That `env` also sets the container
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants