fix(build): validate build options at CLI / MCP entry - #880
Merged
star-e merged 8 commits intoAug 23, 2026
Merged
Conversation
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.
Contributor
Author
Contributor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Backport of the CLI/MCP build option validation work onto
v0.0.1-alpha.39.Every
build()entry that isn't Pink (CLI, MCPbuilder-build, Launcher) now runs the platform's registeredverifyRulesagainst the merged options before handing off to the build pipeline, so obviously bad inputs fail fast with aPARAM_ERROR(32) and a per-field reason list instead of blowing up mid-build.Changes
src/core/builder/index.ts: newverifyBuildOptions()that merges platform defaults, prefills the runtime-computed defaults the oldcheckOptionsused to auto-recover viafixedValue(taskName→ platform name;startScene/scenes→ asset-db defaults;renderPipeline→ project engine config), then runspluginManager.checkBuildOptions(). Onlywarn-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*Requiredrules (e.g.keystoreRequired) so gates that require other fields still fire on empty values.verifyRules:android,google-play: guard keystore path/password/alias/aliasPassword whenuseDebugKeystoreis off;apiLevelgated byandroidInstantand by deferredrenderPipeline.harmonyos-next:packageNameregex synced with the editor;renderBackEnd/orientation/deviceTypesall "at least one enabled".renderBackEnd.supportedintentionally 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 forverifyBuildOptions— skipCheck escape hatch, warn vs. error split,fixedValueno longer silently downgrades, defaultsDeep semantics,taskName/startScene/scenesruntime normalization, asset-db-throws fallback path.src/core/builder/test/verify-build-options-integration.spec.ts: runs the fullpluginManager+ real platform configs to prove the merged-then-checked chain works end-to-end (android, ios, renderPipeline-drivenapiLevelgate).src/core/builder/test/platform-verify-rules.spec.ts: per-platformverifyRuleMapmatrix.src/core/builder/test/validator-manager-shortcircuit.spec.ts: covers the empty-value short-circuit's*Requiredcarve-out.Local run: 4 spec files, 104 tests, all pass. E2E
cocos build web-desktop/windowson the fixture project also go green (previous exit 32 → 0).