Skip to content

fix(lint): accept an empty composable stack - #230

Merged
pjcdawkins merged 3 commits into
mainfrom
fix/lint-empty-stack
Oct 15, 2026
Merged

pjcdawkins merged 3 commits into
mainfrom
fix/lint-empty-stack

Conversation

@pjcdawkins

Copy link
Copy Markdown
Contributor

upsun lint rejected stack: [] for a composable image (Invalid type. Expected: object, given: array), although the platform accepts an empty stack, e.g. for static sites. The platform no longer accepts a non-empty list: stack must be a mapping of runtimes and packages.

  • Allow arrays and null for stack in the Flex and Fixed schemas.
  • Report a non-empty list from CheckTypes with a specific error ('stack' must be a mapping of 'runtimes' and 'packages', not a list) instead of a schema type error. As a semantic check, it's only shown once the config passes the schema.
  • Treat an empty list as an empty stack, and only warn that stack should be specified for a composable image when it is missing or null, not when it is deliberately empty ([], {}, {runtimes: []}).

🤖 Generated with Claude Code

The platform accepts an empty `stack` (`[]`, `{}` or null) for a
composable image, e.g. for static sites, but rejects a non-empty list:
the list form was removed in favor of the `runtimes`/`packages` mapping.

- Allow arrays and null for `stack` in the Flex and Fixed schemas.
- Report a non-empty list with a specific error from CheckTypes, rather
  than a JSON schema type error.
- Treat an empty list as an empty stack, and only warn that 'stack'
  should be specified for a composable image when it is missing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@upsun-dispatch upsun-dispatch Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note

Reviewed — No blocking findings · 🔵 1 minor point

🔍 Full review · 7 files reviewed

🔵 Minor point

  • internal/lint/types.go:54 — A non-empty stack list now gets an error from checkStackList, but the existing checks still treat it as a real stack, so the same problem is reported twice. isStackEmpty returns false for a non-empty list. With a non-composable type (e.g. type: php:8.4, stack: [php@8.4]), checkRuntime also adds the warning "'stack' is only used with a composable image type" (line 47). With an empty type, line 55 also adds "'type' should be specified (as a composable image) when using 'stack'". In both cases a stack the linter has just rejected as malformed gets a second report.
Verification
  • Both schemas use "type": ["object","array","null"], and Flex app and task stacks both point to the single #/definitions/stack, so only lists and null newly pass the schema.
  • CheckContent returns on schema errors before runChecks, so the non-empty list error only appears once the config passes the schema, as the description says.
  • The composable warning now fires only when the stack is nil (missing or stack:); [], {} and {runtimes: [], packages: []} no longer warn.
  • checkStackList runs before the task.Base == "" guard, so tasks using base still get the list check (covered by the types_test stack_list case).

The PR adds table cases in internal/lint/linter_test.go, fixed_test.go and types_test.go for empty stacks and stack lists, and updates two existing cases to the mapping form; CI runs them in the make test-go step of .github/workflows/ci.yml. No test covers a non-empty list combined with a non-composable type or a missing type.

Review details
  • Commit: c3c54ca
  • Model: claude-opus-5-5

Review 1 of 10 for this pull request · View the full run

A non-empty stack list was also treated as a stack by the type checks,
adding the 'only used with a composable image' or 'type should be
specified' warnings after the list error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@upsun-dispatch upsun-dispatch Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Warning

Changes suggested — 🟡 1 warning

🔁 Incremental · 2 files reviewed

Verification
  • Config.Applications and Tasks are maps of struct values, so app.Stack = checkStackList(...) changes a local copy and does not affect the checks that run after CheckTypes.
  • For a composable app with a list stack, the empty-map replacement is non-nil, so the extra warning 'stack' should be specified no longer appears.
  • For a classic or docker app with a list stack, the replacement counts as empty for isStackEmpty, so the list error is not followed by a duplicate warning that 'stack' is only used with a composable image type.

The stack_list case in TestCheckTypes (internal/lint/types_test.go) now covers composable, classic, untyped and base-task apps with list stacks. Its expected output includes the extra type cannot be empty error reported in the finding, so the test locks that behaviour in.

Review details

Review 2 of 10 for this pull request · View the full run

Comment thread internal/lint/types.go
…k list

Replacing the rejected list before the stack-without-type check sent the
app to the type check, which reported 'type cannot be empty'. Check the
original stack there instead, and only skip the warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@pjcdawkins
pjcdawkins merged commit 2e7a528 into main Oct 15, 2026
7 checks passed
@pjcdawkins
pjcdawkins deleted the fix/lint-empty-stack branch October 15, 2026 07:43
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.

1 participant