diff --git a/apps/docs/content/guides/zerops-yaml-advanced.mdx b/apps/docs/content/guides/zerops-yaml-advanced.mdx index 2221bc0b..194d091b 100644 --- a/apps/docs/content/guides/zerops-yaml-advanced.mdx +++ b/apps/docs/content/guides/zerops-yaml-advanced.mdx @@ -113,21 +113,30 @@ zerops: run: { envVariables: { NODE_ENV: production } } ``` -Supports single parent (`extends: base`) or multiple parents (`extends: [base, logging]`) -- later parents override earlier ones: +The child is a deep copy of the resolved parent with its own keys applied on top. The merge is **recursive, key by key**: + +- **Nested objects** (`build`, `run`, `deploy`, `healthCheck`, `readinessCheck`, `routing`, ...): only the keys the child specifies are overwritten, everything else is inherited. `run: { start: x }` in the child keeps the parent's `run.envVariables`, `run.ports`, etc. +- **Maps** (`envVariables`): merged. Child keys are added, same-named keys take the child value, remaining parent keys stay. `KEY: null` removes an inherited variable (`KEY: ""` is a regular empty value); `envVariables: null` drops the whole inherited map. +- **Lists** (`buildCommands`, `prepareCommands`, `initCommands`, `deployFiles`, `ports`, `startCommands`, `crontab`, `cache` as a list): **replaced as a whole**, never appended. Repeat the full list in the child. `[]` clears it. +- **Chains** resolve transitively (`prod` extends `staging` extends `base`) in any file order, up to 25 levels deep. A setup cannot extend itself. +- Must reference another `setup` name in the same file. +- **Multiple parents** (`extends: [base, logging]`): resolved exactly as if `logging` extended `base` and the child extended `logging`. Later parents win over earlier ones (including their own ancestry), the child's own keys win over all parents. Same map-merge / list-replace rules apply at every step. ```yaml zerops: - setup: base - build: { buildCommands: [npm run build], deployFiles: ./dist } - - setup: logging - run: { envVariables: { LOG_LEVEL: info } } + build: { buildCommands: [npm ci, npm run build], deployFiles: ./dist } + run: { start: npm start, envVariables: { LOG_LEVEL: info, NODE_ENV: development } } - setup: prod - extends: [base, logging] - run: { envVariables: { NODE_ENV: production } } + extends: base + build: { buildCommands: [npm ci, npm run build -- --mode=production] } # list replaced, deployFiles inherited + run: { envVariables: { NODE_ENV: production, LOG_LEVEL: null } } # merged: NODE_ENV=production, LOG_LEVEL removed; start inherited + - setup: logging + run: { envVariables: { LOG_LEVEL: debug, LOG_FORMAT: json } } + - setup: prod-debug + extends: [prod, logging] # everything from prod, then LOG_LEVEL=debug and LOG_FORMAT=json from logging; NODE_ENV=production inherited via prod ``` -Configuration is **merged at the section level** -- child values override parent values within each section (build, run, deploy), but unspecified sections inherit from parent. Must reference another `setup` name in the same file. - ## Base Images Available runtimes and versions are listed in **Service Stacks (live)** -- injected by `zerops_knowledge` and workflow responses. Some key rules: diff --git a/apps/docs/content/zerops-yaml/specification.mdx b/apps/docs/content/zerops-yaml/specification.mdx index f58cde9d..12112325 100644 --- a/apps/docs/content/zerops-yaml/specification.mdx +++ b/apps/docs/content/zerops-yaml/specification.mdx @@ -109,9 +109,75 @@ zerops: When using `extends`: - The `extends` value must refer to another service's `setup` value in the same file -- The child service inherits all configuration from the base service -- Configuration is merged at the section level (`build`, `run`, `deploy`) -- You can override specific sections by redefining them +- The child service starts as a full copy of the base service, then its own keys are applied on top +- Merging is recursive, key by key. Redefining `run` in the child does not replace the whole `run` section, only the keys you specify inside it. The same applies to nested objects such as `healthCheck` or `readinessCheck` +- Maps such as `envVariables` are merged: the child's keys are added to the base's keys, and a key defined in both takes the child's value. Setting a key to `null` removes the inherited variable (an empty string `""` is a regular value), and `envVariables: null` drops the whole inherited map +- Lists such as `buildCommands`, `initCommands`, `deployFiles`, `ports` or `startCommands` are replaced as a whole, never appended. If you need to change a list, define the full list in the child +- A base service can itself extend another service. Chains are resolved regardless of the order of services in the file. A service cannot extend itself +- `extends` also accepts a list, for example `extends: [base, logging]`. The child is then resolved exactly as if `logging` extended `base` and the child extended `logging`: later services in the list override earlier ones, and the child's own keys override all of them + +The following example shows how maps and lists behave differently: + +```yaml +zerops: + - setup: base + build: + base: nodejs@22 + buildCommands: + - npm ci + - npm run build + deployFiles: ./dist + run: + base: nodejs@22 + start: npm start + envVariables: + LOG_LEVEL: info + NODE_ENV: development + + - setup: prod + extends: base + build: + buildCommands: + - npm ci + - npm run build -- --mode=production + run: + envVariables: + NODE_ENV: production + LOG_LEVEL: null + + - setup: dev + extends: base + run: + envVariables: + DEBUG: "1" +``` + +The resolved `prod` service is: + +```yaml +setup: prod +build: + base: nodejs@22 # inherited + buildCommands: # list replaced as a whole + - npm ci + - npm run build -- --mode=production + deployFiles: ./dist # inherited +run: + base: nodejs@22 # inherited + start: npm start # inherited + envVariables: # map merged key by key, LOG_LEVEL removed by null + NODE_ENV: production +``` + +And `dev` keeps everything from `base` and adds `DEBUG`: + +```yaml +run: + envVariables: + LOG_LEVEL: info + NODE_ENV: development + DEBUG: "1" +``` :::tip Create a base service with common configuration and extend it for environment-specific services to keep your `zerops.yaml` file DRY (Don't Repeat Yourself). diff --git a/apps/docs/static/llms-full.txt b/apps/docs/static/llms-full.txt index b9fba534..14cc5392 100644 --- a/apps/docs/static/llms-full.txt +++ b/apps/docs/static/llms-full.txt @@ -17075,21 +17075,30 @@ zerops: run: { envVariables: { NODE_ENV: production } } ``` -Supports single parent (`extends: base`) or multiple parents (`extends: [base, logging]`) -- later parents override earlier ones: +The child is a deep copy of the resolved parent with its own keys applied on top. The merge is **recursive, key by key**: + +- **Nested objects** (`build`, `run`, `deploy`, `healthCheck`, `readinessCheck`, `routing`, ...): only the keys the child specifies are overwritten, everything else is inherited. `run: { start: x }` in the child keeps the parent's `run.envVariables`, `run.ports`, etc. +- **Maps** (`envVariables`): merged. Child keys are added, same-named keys take the child value, remaining parent keys stay. `KEY: null` removes an inherited variable (`KEY: ""` is a regular empty value); `envVariables: null` drops the whole inherited map. +- **Lists** (`buildCommands`, `prepareCommands`, `initCommands`, `deployFiles`, `ports`, `startCommands`, `crontab`, `cache` as a list): **replaced as a whole**, never appended. Repeat the full list in the child. `[]` clears it. +- **Chains** resolve transitively (`prod` extends `staging` extends `base`) in any file order, up to 25 levels deep. A setup cannot extend itself. +- Must reference another `setup` name in the same file. +- **Multiple parents** (`extends: [base, logging]`): resolved exactly as if `logging` extended `base` and the child extended `logging`. Later parents win over earlier ones (including their own ancestry), the child's own keys win over all parents. Same map-merge / list-replace rules apply at every step. ```yaml zerops: - setup: base - build: { buildCommands: [npm run build], deployFiles: ./dist } - - setup: logging - run: { envVariables: { LOG_LEVEL: info } } + build: { buildCommands: [npm ci, npm run build], deployFiles: ./dist } + run: { start: npm start, envVariables: { LOG_LEVEL: info, NODE_ENV: development } } - setup: prod - extends: [base, logging] - run: { envVariables: { NODE_ENV: production } } + extends: base + build: { buildCommands: [npm ci, npm run build -- --mode=production] } # list replaced, deployFiles inherited + run: { envVariables: { NODE_ENV: production, LOG_LEVEL: null } } # merged: NODE_ENV=production, LOG_LEVEL removed; start inherited + - setup: logging + run: { envVariables: { LOG_LEVEL: debug, LOG_FORMAT: json } } + - setup: prod-debug + extends: [prod, logging] # everything from prod, then LOG_LEVEL=debug and LOG_FORMAT=json from logging; NODE_ENV=production inherited via prod ``` -Configuration is **merged at the section level** -- child values override parent values within each section (build, run, deploy), but unspecified sections inherit from parent. Must reference another `setup` name in the same file. - ## Base Images Available runtimes and versions are listed in **Service Stacks (live)** -- injected by `zerops_knowledge` and workflow responses. Some key rules: @@ -44499,9 +44508,75 @@ zerops: When using `extends`: - The `extends` value must refer to another service's `setup` value in the same file -- The child service inherits all configuration from the base service -- Configuration is merged at the section level (`build`, `run`, `deploy`) -- You can override specific sections by redefining them +- The child service starts as a full copy of the base service, then its own keys are applied on top +- Merging is recursive, key by key. Redefining `run` in the child does not replace the whole `run` section, only the keys you specify inside it. The same applies to nested objects such as `healthCheck` or `readinessCheck` +- Maps such as `envVariables` are merged: the child's keys are added to the base's keys, and a key defined in both takes the child's value. Setting a key to `null` removes the inherited variable (an empty string `""` is a regular value), and `envVariables: null` drops the whole inherited map +- Lists such as `buildCommands`, `initCommands`, `deployFiles`, `ports` or `startCommands` are replaced as a whole, never appended. If you need to change a list, define the full list in the child +- A base service can itself extend another service. Chains are resolved regardless of the order of services in the file. A service cannot extend itself +- `extends` also accepts a list, for example `extends: [base, logging]`. The child is then resolved exactly as if `logging` extended `base` and the child extended `logging`: later services in the list override earlier ones, and the child's own keys override all of them + +The following example shows how maps and lists behave differently: + +```yaml +zerops: + - setup: base + build: + base: nodejs@22 + buildCommands: + - npm ci + - npm run build + deployFiles: ./dist + run: + base: nodejs@22 + start: npm start + envVariables: + LOG_LEVEL: info + NODE_ENV: development + + - setup: prod + extends: base + build: + buildCommands: + - npm ci + - npm run build -- --mode=production + run: + envVariables: + NODE_ENV: production + LOG_LEVEL: null + + - setup: dev + extends: base + run: + envVariables: + DEBUG: "1" +``` + +The resolved `prod` service is: + +```yaml +setup: prod +build: + base: nodejs@22 # inherited + buildCommands: # list replaced as a whole + - npm ci + - npm run build -- --mode=production + deployFiles: ./dist # inherited +run: + base: nodejs@22 # inherited + start: npm start # inherited + envVariables: # map merged key by key, LOG_LEVEL removed by null + NODE_ENV: production +``` + +And `dev` keeps everything from `base` and adds `DEBUG`: + +```yaml +run: + envVariables: + LOG_LEVEL: info + NODE_ENV: development + DEBUG: "1" +``` :::tip Create a base service with common configuration and extend it for environment-specific services to keep your `zerops.yaml` file DRY (Don't Repeat Yourself). diff --git a/apps/docs/static/llms-small.txt b/apps/docs/static/llms-small.txt index 3271d881..ecb55743 100644 --- a/apps/docs/static/llms-small.txt +++ b/apps/docs/static/llms-small.txt @@ -16766,21 +16766,30 @@ zerops: run: { envVariables: { NODE_ENV: production } } ``` -Supports single parent (`extends: base`) or multiple parents (`extends: [base, logging]`) -- later parents override earlier ones: +The child is a deep copy of the resolved parent with its own keys applied on top. The merge is **recursive, key by key**: + +- **Nested objects** (`build`, `run`, `deploy`, `healthCheck`, `readinessCheck`, `routing`, ...): only the keys the child specifies are overwritten, everything else is inherited. `run: { start: x }` in the child keeps the parent's `run.envVariables`, `run.ports`, etc. +- **Maps** (`envVariables`): merged. Child keys are added, same-named keys take the child value, remaining parent keys stay. `KEY: null` removes an inherited variable (`KEY: ""` is a regular empty value); `envVariables: null` drops the whole inherited map. +- **Lists** (`buildCommands`, `prepareCommands`, `initCommands`, `deployFiles`, `ports`, `startCommands`, `crontab`, `cache` as a list): **replaced as a whole**, never appended. Repeat the full list in the child. `[]` clears it. +- **Chains** resolve transitively (`prod` extends `staging` extends `base`) in any file order, up to 25 levels deep. A setup cannot extend itself. +- Must reference another `setup` name in the same file. +- **Multiple parents** (`extends: [base, logging]`): resolved exactly as if `logging` extended `base` and the child extended `logging`. Later parents win over earlier ones (including their own ancestry), the child's own keys win over all parents. Same map-merge / list-replace rules apply at every step. ```yaml zerops: - setup: base - build: { buildCommands: [npm run build], deployFiles: ./dist } - - setup: logging - run: { envVariables: { LOG_LEVEL: info } } + build: { buildCommands: [npm ci, npm run build], deployFiles: ./dist } + run: { start: npm start, envVariables: { LOG_LEVEL: info, NODE_ENV: development } } - setup: prod - extends: [base, logging] - run: { envVariables: { NODE_ENV: production } } + extends: base + build: { buildCommands: [npm ci, npm run build -- --mode=production] } # list replaced, deployFiles inherited + run: { envVariables: { NODE_ENV: production, LOG_LEVEL: null } } # merged: NODE_ENV=production, LOG_LEVEL removed; start inherited + - setup: logging + run: { envVariables: { LOG_LEVEL: debug, LOG_FORMAT: json } } + - setup: prod-debug + extends: [prod, logging] # everything from prod, then LOG_LEVEL=debug and LOG_FORMAT=json from logging; NODE_ENV=production inherited via prod ``` -Configuration is **merged at the section level** -- child values override parent values within each section (build, run, deploy), but unspecified sections inherit from parent. Must reference another `setup` name in the same file. - ## Base Images Available runtimes and versions are listed in **Service Stacks (live)** -- injected by `zerops_knowledge` and workflow responses. Some key rules: @@ -37509,9 +37518,75 @@ zerops: When using `extends`: - The `extends` value must refer to another service's `setup` value in the same file -- The child service inherits all configuration from the base service -- Configuration is merged at the section level (`build`, `run`, `deploy`) -- You can override specific sections by redefining them +- The child service starts as a full copy of the base service, then its own keys are applied on top +- Merging is recursive, key by key. Redefining `run` in the child does not replace the whole `run` section, only the keys you specify inside it. The same applies to nested objects such as `healthCheck` or `readinessCheck` +- Maps such as `envVariables` are merged: the child's keys are added to the base's keys, and a key defined in both takes the child's value. Setting a key to `null` removes the inherited variable (an empty string `""` is a regular value), and `envVariables: null` drops the whole inherited map +- Lists such as `buildCommands`, `initCommands`, `deployFiles`, `ports` or `startCommands` are replaced as a whole, never appended. If you need to change a list, define the full list in the child +- A base service can itself extend another service. Chains are resolved regardless of the order of services in the file. A service cannot extend itself +- `extends` also accepts a list, for example `extends: [base, logging]`. The child is then resolved exactly as if `logging` extended `base` and the child extended `logging`: later services in the list override earlier ones, and the child's own keys override all of them + +The following example shows how maps and lists behave differently: + +```yaml +zerops: + - setup: base + build: + base: nodejs@22 + buildCommands: + - npm ci + - npm run build + deployFiles: ./dist + run: + base: nodejs@22 + start: npm start + envVariables: + LOG_LEVEL: info + NODE_ENV: development + + - setup: prod + extends: base + build: + buildCommands: + - npm ci + - npm run build -- --mode=production + run: + envVariables: + NODE_ENV: production + LOG_LEVEL: null + + - setup: dev + extends: base + run: + envVariables: + DEBUG: "1" +``` + +The resolved `prod` service is: + +```yaml +setup: prod +build: + base: nodejs@22 # inherited + buildCommands: # list replaced as a whole + - npm ci + - npm run build -- --mode=production + deployFiles: ./dist # inherited +run: + base: nodejs@22 # inherited + start: npm start # inherited + envVariables: # map merged key by key, LOG_LEVEL removed by null + NODE_ENV: production +``` + +And `dev` keeps everything from `base` and adds `DEBUG`: + +```yaml +run: + envVariables: + LOG_LEVEL: info + NODE_ENV: development + DEBUG: "1" +``` :::tip Create a base service with common configuration and extend it for environment-specific services to keep your `zerops.yaml` file DRY (Don't Repeat Yourself).