Skip to content

fix(build): validate build options at CLI / MCP entry - #880

Merged
star-e merged 8 commits into
cocos:v0.0.1-alpha.39from
qiuguohua:fix/build-platform-param-validation-alpha39
Aug 23, 2026
Merged

fix(build): validate build options at CLI / MCP entry#880
star-e merged 8 commits into
cocos:v0.0.1-alpha.39from
qiuguohua:fix/build-platform-param-validation-alpha39

Conversation

@qiuguohua

Copy link
Copy Markdown
Contributor

Summary

Backport of the CLI/MCP build option validation work onto v0.0.1-alpha.39.

Every build() entry that isn't Pink (CLI, MCP builder-build, Launcher) now runs the platform's registered verifyRules against the merged options before handing off to the build pipeline, so obviously bad inputs fail fast with a PARAM_ERROR (32) and a per-field reason list instead of blowing up mid-build.

Changes

  • src/core/builder/index.ts: new verifyBuildOptions() that merges platform defaults, prefills the runtime-computed defaults the old checkOptions used to auto-recover via fixedValue (taskName → platform name; startScene / scenes → asset-db defaults; renderPipeline → project engine config), then runs pluginManager.checkBuildOptions(). Only warn-level failures pass through; anything else returns { code: PARAM_ERROR, reason }.
  • src/core/launcher.ts, src/api/builder/builder.ts, src/commands/build.ts: wire the gate into every non-Pink entry and surface the reason string in CLI output.
  • src/core/builder/share/validator-manager.ts: keep the "skip empty" fast path but honor conditional *Required rules (e.g. keystoreRequired) so gates that require other fields still fire on empty values.
  • Platform configs — add / tighten the platform-specific verifyRules:
    • android, google-play: guard keystore path/password/alias/aliasPassword when useDebugKeystore is off; apiLevel gated by androidInstant and by deferred renderPipeline.
    • harmonyos-next: packageName regex synced with the editor; renderBackEnd / orientation / deviceTypes all "at least one enabled". renderBackEnd.supported intentionally limited to ['gles3'] — vulkan / gles2 haven't been validated on HarmonyOS Next yet, comment marks where to open it back up.
    • ios, ohos, windows: cover the remaining required fields.

Tests

  • src/core/builder/test/verify-build-options.spec.ts: unit coverage for verifyBuildOptions — skipCheck escape hatch, warn vs. error split, fixedValue no longer silently downgrades, defaultsDeep semantics, taskName / startScene / scenes runtime normalization, asset-db-throws fallback path.
  • src/core/builder/test/verify-build-options-integration.spec.ts: runs the full pluginManager + real platform configs to prove the merged-then-checked chain works end-to-end (android, ios, renderPipeline-driven apiLevel gate).
  • src/core/builder/test/platform-verify-rules.spec.ts: per-platform verifyRuleMap matrix.
  • src/core/builder/test/validator-manager-shortcircuit.spec.ts: covers the empty-value short-circuit's *Required carve-out.

Local run: 4 spec files, 104 tests, all pass. E2E cocos build web-desktop / windows on the fixture project also go green (previous exit 32 → 0).

CLI and MCP paths bypassed the panel's UI validation, so invalid params
(e.g. malformed android packageName) silently reached the build.
Add verifyBuildOptions in core/builder that runs pluginManager.checkBuildOptions
and is invoked from Launcher.build (CLI) and BuilderApi.build (MCP).
Pink stays untouched — it already validates via its UI layer.

Errors with a fixedValue are downgraded to warn to preserve the
existing default-fallback behaviour from createBuildTask.checkOptions.

Tests: 9 cases covering skipCheck, all-valid, warn-only, error aggregation,
error-with-fixedValue downgrade, hard error without fixedValue, missing
message fallback, checker exception, and undefined options.
Editor's getVerifyMap for android checks keystorePath / keystorePassword /
keystoreAlias / keystoreAliasPassword must be non-empty when
useDebugKeystore is false. CLI's android platform config had no verifyRules
for these fields, so releases with a missing custom keystore silently
proceeded until the packaging step failed.

Add a shared 'keystoreRequired' rule to android verifyRuleMap that
short-circuits when useDebugKeystore is true; wire it into the four
keystore fields via verifyRules. Regex packageName rule unchanged.
Mirror of the android change: editor's google-play getVerifyMap requires
keystorePath / keystorePassword / keystoreAlias / keystoreAliasPassword
to be non-empty when useDebugKeystore is false. CLI had no verifyRules
for these fields.

Add 'keystoreRequired' rule reading options.packages['google-play'].useDebugKeystore,
wire it into the four keystore fields via verifyRules.
Editor's harmonyos-next packageName check has four gates: not empty,
regex format, length 7..128, and no HarmonyOS reserved keywords in any
segment (openharmony/harmonyos/harmony/system/ohos/oh). CLI only ran the
regex, so package names like 'com.harmony.app' would pass the CLI and
later fail at the HAP signing / AGC upload step.

Extend the packageName rule func to include the length check and the
token-aware keyword check (dot-segments, word-boundary, case-insensitive).
appABIs rule unchanged.
Entry-layer validation (verifyBuildOptions):
- merge the platform defaults into the options before checking, so a field
  the caller omits falls back to its platform default instead of failing
- an explicitly passed invalid value is now a hard failure (PARAM_ERROR,
  exit code 32). The previous "downgrade to a warning when the rule reports
  a fixedValue" path is removed, so a build can no longer start with a
  silently substituted value. Pass skipCheck to bypass validation entirely
- resolve renderPipeline from the engine config: it is a project setting
  that checkProjectSetting only fills inside the build task, so any rule
  depending on it (apiLevelRenderPipeline) could never fire at the entry.
  This matches the editor, which reads the project profile directly

Rules migrated from the editor verificationFunc / getVerifyMap into
verifyRuleMap, the only validation entry the CLI has:
- ios: targetVersionStyle / targetVersionTaskFlow / targetVersionMin,
  orientation, osTarget
- windows, harmonyos-next: renderBackEnd (at least one supported backend)
- ohos, harmonyos-next: orientation; harmonyos-next: deviceTypes, appABIs
- harmonyos-next renderBackEnd now exposes vulkan / gles3 / gles2 and
  declares an object-level default, so the value is no longer undefined

Fixes found while migrating:
- android / google-play / huawei-agc: the linked gates (useDebugKeystore,
  JobSystem, resizeableActivity, androidInstant) read
  options.packages[options.platform] instead of a hardcoded packages.android.
  huawei-agc inherits the android config, so the hardcoded path made every
  huawei-agc build fail with PARAM_ERROR on the keystore rules
- android / google-play: apiLevelTbb read packages.native.JobSystem, a path
  that does not exist in the CLI, so the rule never fired
- ios: use a segment-wise version compare, the previous helper padded only
  the first separator and judged '9.10' >= '11.0'
- validator-manager: empty values also reach rules whose name ends with
  Required, which conditional required rules need
- google-play: log why the API level list is empty (no SDK path, no
  platforms directory, or nothing installed at or above the Google Play
  minimum API level 24)

Tests: platform rule specs, an entry-layer integration spec that drives the
real pluginManager with the real platform configs, and the validator
empty-value short-circuit convention.
verifyBuildOptions was rejecting builds whose common options relied on
values computed from project state at build time:

- taskName defaults to '' with verifyRules ['required']; createBuildTask
  falls back to platform, but that fallback runs inside build() — after
  verifyBuildOptions had already returned PARAM_ERROR.
- startScene / scenes defaults are '' / [] but the real defaults come
  from asset-db via getDefaultStartScene / getDefaultScenes; the old
  checkOptions path recovered via fixedValue, which the new entry gate
  intentionally does not consume.

Fill these three fields on the merged options before running the
schema check, matching what the downstream build path already does.
Wrap the asset-db calls in try/catch so unit / uninitialized-engine
paths keep working.

Add unit coverage for the taskName / startScene / scenes normalization,
including the user-provided values still winning and the asset-db-throws
fallback path.
vulkan / gles2 haven't been validated on HarmonyOS Next yet, so
renderBackEnd's verifyRule intentionally keeps 'supported' at
['gles3']. Leave the full ['vulkan', 'gles3', 'gles2'] list in a
comment as a reminder to open it back up once those two backends
are proven stable.
…ted list

The docs commit narrowed renderBackEnd's `supported` array to
['gles3'] but this spec still expected vulkan / gles2 alone to
pass. Flip those two assertions to false and leave a comment
pointing at where the expectation should be opened back up when
vulkan / gles2 are validated.
@qiuguohua

Copy link
Copy Markdown
Contributor Author

#874

@qiuguohua
qiuguohua requested a review from star-e August 21, 2026 07:53
@star-e
star-e merged commit 547d35f into cocos:v0.0.1-alpha.39 Aug 23, 2026
4 checks passed
@star-e

star-e commented Aug 23, 2026

Copy link
Copy Markdown
Contributor
image npm run init failed.

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.

2 participants