Repository navigation
fix(schema): sync Upsun config schema with the Upsun CLI - #220
Conversation
Replace resources/validation/upsun.json (served at /schema/upsun) with the schema maintained by the Upsun CLI for `upsun lint` (internal/lint/schema/upsun-config-schema.json), which has been corrected against what the platform accepts. Notably, `stack` is now a mapping with `runtimes` and `packages` keys, or empty. Meta-specific changes on top of the CLI copy: - Keep the $refs to /schema/runtime-versions and /schema/service-versions for application and service types. - Also accept `composable:*` application types, which the runtime-versions enum does not include, so composable apps were always rejected. - Add `maxItems: 0` to `stack`, so a non-empty (legacy) list is rejected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The runtime-versions enum has no docker entries, so applications using a `docker:` type with the `image` key were rejected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Warning
Changes suggested — 🟡 1 warning · 🔵 3 minor points
🔍 Full review · 1 file reviewed
Verification
- I ran the merged schema through a draft-07 validator with the runtime and service refs stubbed:
stack: [],{}and null pass, andstack: ["php@8.4"]andstack: "php"fail. composable:25.05with a runtimes/packages stack validates, and an unknown runtime type such asfoo:1is rejected by theanyOfon applicationtype.- Top-level
.xanchor keys pass and unknown top-level keys fail, as the newpatternProperties/additionalProperties: falseintend. - The served schema still points at
https://meta.upsun.com/schema/runtime-versionsandservice-versions, and both endpoints return{type: string, enum}snippets that match those refs.
The diff adds no tests. The Validate Upsun config.yaml schema job (npm run validate:upsun) in validate-registry.yml only checks that the file is JSON with a loose zod shape. It does not compile the schema or validate any sample configs, so nothing checks the new semantics.
Review details
- Commit: 6c52363
- Model: claude-opus-5-5
Review 1 of 10 for this pull request · View the full run
- Fix the broken docs URL in the hooks.deploy description. - Remove the object default on the string service type. - Check task types against the runtime versions, like application types. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Note
Reviewed — No new issues found · 1 still open
🔁 Incremental · 1 file reviewed
Outstanding from earlier reviews:
- 🟡 #4280670992 —
resources/validation/upsun.json:334: Users get misplaced config that validates cleanly but does nothing (or fails at deploy), which defeats the point of the schema. — Not fixed:authorizations,egressandcontainer_profileare still listed underapplications.*.web.properties(lines 334–342), so the schema still accepts them aswebkeys.
Verification
tasks.*.typenow uses the same runtime-versions$ref/^(composable|docker):anyOf asapplications.*.typeat lines 12–20.- The
hooks.deploydescription URL now matches itsbuildandpost_deploysiblings, and no\tsequences remain anywhere in the file. - The service
typeno longer carries the objectdefault: {}, and the file still parses as valid JSON.
Nothing in the diff tests these schema edits, and no test checks that upsun.json validates sample configs. I checked only that the file still parses as JSON.
Review 2 of 10 for this pull request · View the full run
Sync with upsun/cli#234: - Accept additional_hosts, firewall and operations under web, and additional_hosts and operations under workers, as overrides of the application's values. Reject egress.build on workers. - Describe override keys as overrides, without defaults. - Remove defaults that fail their own schema. The meta-specific type references and the stack maxItems are kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sync with upsun/cli#234, which removes `firewall` (deprecated in favor of `egress`) from applications, web, workers and services. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
I ran this schema against a set of configurations the platform's validator accepts, with Upsun settings (sizing API and unified configuration on). It rejects these, all of which validate on the platform: Explicit applications:
app:
type: php:8.4
web:
size: null
locations:
/:
passthru: null
operations:
op:
timeout: null
commands:
start: echo
egress:
runtime:
allowed_domains:
- host: api.example.com
port: null
routes:
"https://{default}/":
type: upstream
upstream: app:http
.note: xOther shapes:
The Written by Claude Code. |
There was a problem hiding this comment.
Warning
Changes suggested — 🟡 1 warning · 🔵 1 minor point
🔁 Incremental (head + base moved) · 1 file reviewed
Verification
- The
weband worker property lists now reference the new sharedadditional_hostsandoperationsdefinitions, and both definitions resolve withindefinitions. egress.buildandegress.runtimeboth resolve to the newegress_phasedefinition, which has the same host/port shape as the inline objects it replaces.- The
firewallkey is removed from the app properties, the worker properties, the property-order list, the service properties anddefinitions, and no#/definitions/firewallreference is left. - The new
max_request_sizedefault"250m"matches that property'stype: string; the old integer default did not.
The diff adds no tests. backend/src/scripts/validate-upsun.ts only checks that upsun.json is valid JSON and has the expected schema structure, so nothing tests that sample configs are accepted or rejected as intended.
Review 3 of 10 for this pull request · View the full run
- Build-phase egress is read from the application only, so a `build` allowlist under `web.egress` has no effect. Use the runtime-only egress definition (renamed `container_egress`) for web as well as workers. - Draft-07 ignores keywords beside `$ref`, so the override descriptions were not shown. Move each `$ref` into an `allOf`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copies the CLI's schema, which now accepts configs the platform accepts: - Explicit null on optional fields, where the platform treats it as unset. - Keys starting with "." in mappings with fixed keys and in the applications, services and routes sections (YAML anchors and notes). - An integer `request_buffering.max_request_size`, and `dynamic_compression` on locations and rules. - Files without `applications`, as files in .upsun are merged. - Any container profile name, as profiles are per project. The web `egress.build` rejection and the `allOf` wrapping of `$ref`s are now in the CLI's copy too. The runtime and service version `$ref`s, the `composable:`/`docker:` pattern and the stack `maxItems: 0` are kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
📋 PR Summary Replaces Changes
|
There was a problem hiding this comment.
Warning
Changes suggested — 🟡 1 warning
🔁 Incremental (head + base moved) · 1 file reviewed
Verification
- Each new
anyOf: [{$ref}, {type: null}]keeps its siblingdescriptionoutside the$ref, so draft-07 still applies the description. container_profilenow accepts any string or null and keeps the four built-in profiles only asexamples, so custom project profiles validate.- Every
additionalProperties: falseobject except thestackdefinition now has the^\.patternProperties escape; I checked this by walking the schema with a script. - Dropping the root
required: [applications]lets a config with onlyroutesorservicesvalidate.
The diff adds no tests or fixtures for the served schema, and no test exercises the new null, anchor-key or integer max_request_size cases. I checked these cases by hand with a draft-07 validator in scratch. The PR description still lists null fields, nested . keys, integer max_request_size, rule-level dynamic_compression, configs without applications, and custom profiles as rejected, but this push now accepts all of them.
Review 5 of 10 for this pull request · View the full run
…fig (#248) * feat(lint): use the schema from meta.upsun.com as the canonical Flex schema The Flex config schema now comes from https://meta.upsun.com/schema/upsun (the upsun/meta repository), which was synced with the CLI's copy in upsun/meta#220. Schema changes are made there first. - Add `make update-schema` (internal/lint/schema/gen.go), which fetches the schema and indents it, and `make check-schema`, run in the non-blocking upstream CI job beside `check-registry` (renamed `lint-upstream`). - Replace the schema's remote $refs (runtime-versions, service-versions) with empty schemas when loading, so lint does not need the network. Types are checked against the registry by CheckTypes. A test fails if the schema gains another remote $ref. - Report the schema's "maxItems: 0" error on a stack list with the same message as checkStackList. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(lint): leave stack lists to checkStackList, so semantic checks still run A schema error stops CheckContent before the semantic checks, so reporting the schema's "maxItems: 0" stack error hid every other error until the stack was fixed. The schema error is now skipped, and checkStackList reports the stack list with the other errors, as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(lint): report Flex stack lists with the schema's own error Use the upstream schema's "maxItems: 0" error for a non-empty stack list, rather than skipping it for checkStackList, which now only reports stack lists in Fixed config. As with any schema error, the semantic checks run once it is fixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(lint): check Fixed config with the Flex schema and an overlay The platform validates Fixed and Flex config with the same models, so Fixed config is now checked with the upstream Flex schema, patched by fixed-overlay.json (a JSON Merge Patch), instead of three schemas maintained here, which had fallen behind (e.g. post_start, dynamic_compression, runtime extensions). The overlay adds the keys that only Fixed config accepts: `disk` on applications, web, workers and services, an application `size` and `name` (required, as on the platform), and `build.caches`. Behavior changes for Fixed config, matching Flex and the platform: - `firewall` is rejected (deprecated in favor of `egress`). - `type` is required, also with a `stack`. - A non-empty stack list gets the schema's error, so checkStackList and the stack-without-type warning are removed. The schema tests now also check the patched schema. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Updates
resources/validation/upsun.json, served atGET /schema/upsun, which had drifted from what the platform accepts.The file is replaced with the schema maintained by the Upsun CLI for
upsun lint(internal/lint/schema/upsun-config-schema.json), from upsun/cli#230, upsun/cli#234 and upsun/cli#242. Main changes:stackis a mapping withruntimesandpackageskeys, or empty ([],{}or null). The legacy string and list forms are rejected. Each runtime is a string or a single-key mapping; each package is a string or a mapping with apackagekey.tasks,image,egress,authorizations,post_start, operationtimeout/stop, thelocalmount source,dynamic_compressionon locations and rules, and null TLS values..(YAML anchors and notes), which the platform ignores at the top level, in theapplications,servicesandroutessections, and in every mapping with fixed keys exceptstack.nullis accepted on the optional fields where the platform treats it as unset (e.g.web.size, locationpassthru, operationtimeout, egressport).applicationsis no longer required, as files in.upsun/are merged and a project may have none.container_profileaccepts any name, as profiles are per project; the standard ones are listed asexamples.request_buffering.max_request_sizeaccepts an integer.web.commands.startis no longer required.weband workers accept the application keys the platform lets them override, exceptdisk, which Upsun sets through the resources API (addsadditional_hostsandoperations), described as overrides and without defaults.egress.buildis rejected on both: build-phase egress is only read from the application.$refwith a sibling description is wrapped inallOf, since draft-07 ignores keywords beside$ref.firewallkey in favor ofegress.services.*.type: {}), which editors insert on completion, and a tab in thehooks.deploydescription.Changes on top of the CLI's copy:
$refs to/schema/runtime-versionsand/schema/service-versionsfor application, task and service types (the CLI checks types in code instead).composable:*anddocker:*application and task types. Neither is in the runtime-versions enum, so every composable app was previously rejected.maxItems: 0tostack, so a non-empty list such asstack: ["php@8.4"]fails. The CLI rejects this in code with a clearer message; in JSON Schema the error is "must NOT have more than 0 items".As with the CLI's schema, some cross-field rules are not expressed here and are only checked by
upsun lint, e.g. taskbase/runcombinations and authorizationaction/resourceby type.🤖 Generated with Claude Code