Skip to content

setup.yml

Interactive setup questions for fresh projects.

workspace/setup.yml defines interactive prompts that run when a developer first enters a fresh project (one without a workspace/local.yml or with an empty one). The wizard collects answers, writes them into workspace/local.yml as merged settings, and then proceeds to deployment.

Use setup questions for one-time per-developer configuration:

  • API keys or secrets (stored in local.yml, which is gitignored)
  • Service toggles (which optional tools the developer wants to enable)
  • Port overrides (when local port conflicts exist)
  • Custom paths or hostnames

The setup wizard is part of the broader dwe deploy flow — running dwe deploy with no subcommand opens an interactive menu that includes a Wizard option when setup questions are present.

  1. Developer runs dwe deploy in an interactive terminal on a fresh project.
  2. The CLI probes for port conflicts and loads workspace/setup.yml (if present).
  3. If both are empty (no questions, no conflicts), no wizard runs — proceed directly to deploy.
  4. If either has content, the menu opens with a Wizard option.
  5. Wizard runs:
    • First, port-conflict prompts (if any) — developer chooses override ports.
    • Then, setup questions (if any) — developer answers each prompt.
    • Finally, both answers are deep-merged into workspace/local.yml and written atomically.
  6. Config is reloaded from the updated local.yml, preflight runs, and deploy proceeds normally.

If the developer cancels at any wizard step (Ctrl-C), local.yml is left untouched — no partial writes.

questions:
- id: api-key
title: GitHub Token
description: Personal access token for private repos (optional)
type: input
required: false
writes: vars.db.api_key
- id: enable-postgres
title: Enable PostgreSQL?
type: confirm
required: false
writes: services.postgres.enabled
- id: http-port
title: Web server port
description: Port to run the local app on
type: input
required: true
writes: services.web.ports.http
validate:
preset: port
- id: select-locale
title: Preferred language
type: select
required: true
writes: vars.app.locale
options:
- value: en
label: English
- value: fr
label: Français
- value: de
label: Deutsch
- id: enable-caching
title: Use Redis caching?
type: confirm
writes: vars.app.cache.enable

The file is optional. When absent, the wizard (if invoked) handles port conflicts only.

FieldTypeRequiredDescription
questionslistyesQuestion entries (see below). May be empty.

Unknown top-level fields are rejected at load time (strict decoding).

FieldTypeRequiredDescription
idstringyesUnique identifier for this question. Used as the key when collecting answers.
typestringyesOne of input, select, multiselect, confirm. Unknown values are rejected by validation.
titlestringyesPrompt text shown to the developer.
descriptionstringnoLonger explanation shown below the title.
requiredboolnoIf true (default false), the wizard enforces a non-empty answer before proceeding. For confirm, required is ignored (always optional).
writesstringyesDot-path where the answer is stored in local.yml. Must be unique across all questions. See Write scope rules.
optionslistnoValid for select and multiselect only. List of {value, label} pairs. Required for both types.
validateobjectnoOptional validation rules. Has two fields (mutually exclusive): preset (a named preset like port / hostname) or regex (a regular expression pattern). Only meaningful for type: input.

Schema rules enforced at load time:

  • id must be unique across entries.
  • writes must be unique across entries and follow dot-path syntax rules (see below).
  • Unknown top-level fields inside a question are rejected.

Schema rules enforced by validation (run via dwe validate):

  • type must be one of the four known values.
  • writes must follow the scope and syntax rules below.
  • validate.preset and validate.regex cannot both be set.
  • validate.* is only meaningful for type: input; setting either on select, multiselect, or confirm is an error.
  • select and multiselect must have non-empty options with unique, non-empty value strings.
  • Service-overlay writes have type consistency rules (see Write scope rules).

A free-text input field with optional validation.

Returns: string (or int if a numeric preset is used — see Validation presets)

Example:

- id: db-password
type: input
title: Database password
required: true
writes: vars.db.password

A single-choice dropdown. Developer picks one value.

Returns: string (the chosen option’s value)

Example:

- id: log-level
type: select
title: Logging level
required: true
writes: vars.app.log_level
options:
- value: debug
label: Debug (verbose)
- value: info
label: Info (normal)
- value: error
label: Error (quiet)

A multi-choice list. Developer picks zero or more values.

Returns: []string (slice of chosen value fields)

Example:

- id: plugins
type: multiselect
title: Plugins to enable
writes: vars.app.plugins
options:
- value: auth
label: Authentication
- value: logging
label: Logging
- value: metrics
label: Metrics

A yes/no toggle.

Returns: bool (true for yes, false for no)

Example:

- id: enable-debug
type: confirm
title: Enable debug mode?
writes: vars.app.debug

Note: required: true on a confirm is a no-op and produces a validation warning. A confirm always yields a valid answer (either true or false).

Presets are shorthand validators for common patterns. Each preset defines what values are accepted AND what Go type is written to local.yml.

Use validate: { preset: <name> } inside a question’s validate block.

Validates a port number (1–65535) and writes an int.

- id: http-port
type: input
title: Web server port
writes: services.web.ports.http
validate:
preset: port

The developer’s input "8080" is stored as the integer 8080 in local.yml, so templates can use it as a number.

Validates a DNS hostname (RFC 1123 short-name format) and writes a string.

- id: postgres-host
type: input
title: Postgres hostname
writes: services.postgres.hosts.internal
validate:
preset: hostname

Validates a non-empty filesystem path and writes a string.

- id: workspace-dir
type: input
title: Workspace directory
writes: vars.app.workspace
validate:
preset: path

Validates that the input is not blank (whitespace-only is rejected) and writes a string.

- id: api-key
type: input
title: API key
writes: vars.db.api_key
validate:
preset: non-empty

If neither preset nor regex is set, the input is accepted as-is (any non-empty string when required: true, any string otherwise).

- id: app-name
type: input
title: Application name
writes: vars.app.name
# No validation; any input is accepted

Validate the input against a regular expression pattern. The pattern is matched as an un-anchored Go regex (substring match); add ^ and $ anchors yourself to require a full match.

- id: email
type: input
title: Email address
writes: vars.user.email
validate:
regex: "^[a-z0-9+._-]+@[a-z0-9.-]+$"

Pattern must compile as a valid Go regex. Invalid patterns are caught by dwe validate before the wizard ever runs.

The writes: field is a dot-path that determines where in workspace/local.yml the answer is stored. Not all paths are allowed — the wizard enforces rules to ensure answers merge safely with the config schema.

These top-level keys are reserved and cannot be written by the wizard:

  • info.* — immutable project metadata
  • styles.* — UI color configuration
  • docker.* — engine policy configuration

Attempting to write to any of these triggers a validation error.

When writing under services.<name>., only three exact leaf paths are allowed:

PathTypeQuestion typeDescription
services.<name>.enabledboolconfirm (required)Toggle service enabled state.
services.<name>.ports.<port_name>intinput with preset: port (required)Override a declared service port.
services.<name>.hosts.<host_name>stringinput (any preset OK)Override a declared service hostname.

Examples of allowed writes:

  • services.web.enabled — must come from a type: confirm question
  • services.web.ports.http — must come from a type: input with validate.preset: port
  • services.postgres.hosts.internal — can come from any type: input

Examples of forbidden writes:

  • services.web (missing the leaf .enabled / .ports.X / .hosts.X) — would overwrite the entire service config
  • services.web.ports (missing the specific port name) — would overwrite all ports
  • services.web.container — not in the allowed leaf set
  • services.web.ports.http from a type: select — wrong question type for the path

Validation error messages cite the specific constraint that failed (e.g., “service ports require type: input with validate.preset: port”).

Custom values belong under the vars: sandbox. The root of the merged config is strict — a top-level db: / app: / custom: key in local.yml is rejected at load time — so write custom answers under vars.*, where any nesting is allowed and any question type is fine:

- writes: vars.db.name # ✓ allowed
- writes: vars.db.connection.host # ✓ allowed
- writes: vars.db.connection.port # ✓ allowed
- writes: vars.app.feature_flags # ✓ allowed
- writes: vars.custom.setting # ✓ allowed

The wizard writes the typed answer value verbatim (string for input / select, bool for confirm, slice for multiselect) and trusts the consuming config (templates, exports, etc.) to handle it appropriately.

questions: []

If port conflicts exist, the wizard opens and prompts for overrides. No question entries needed.

questions:
- id: github-token
type: input
title: GitHub personal access token
description: Used for private repo access. Leave blank to skip.
required: false
writes: vars.secrets.github_token
validate:
preset: non-empty
- id: enable-postgres
type: confirm
title: Enable PostgreSQL?
writes: services.postgres.enabled
questions:
- id: database-host
type: input
title: Database hostname
description: The address where your database lives
required: true
writes: services.postgres.hosts.internal
validate:
preset: hostname
- id: db-port
type: input
title: Database port
required: true
writes: services.postgres.ports.db
validate:
preset: port
questions:
- id: enabled-plugins
type: multiselect
title: Plugins to enable
description: Select any combination (space to toggle, enter to confirm)
required: false
writes: vars.app.plugins
options:
- value: auth
label: Authentication
- value: analytics
label: Analytics
- value: export
label: Export to S3
- value: webhooks
label: Webhooks
questions:
- id: workspace-root
type: input
title: Workspace root directory
required: true
writes: vars.workspace.root
validate:
preset: path
- id: cache-backend
type: select
title: Cache backend
required: true
writes: vars.cache.backend
options:
- value: redis
label: Redis
- value: memcached
label: Memcached
- value: local
label: Local (in-memory, not persistent)
- id: enable-profiling
type: confirm
title: Enable performance profiling?
writes: vars.debug.profiling
  • dwe deploy — opens the wizard menu on fresh projects
  • dwe validate — checks workspace/setup.yml schema and writes paths
  • dwe validate setup — validates only the setup domain