fix(build): validate platform build options at the CLI/MCP entry - #874
Open
qiuguohua wants to merge 9 commits into
Open
fix(build): validate platform build options at the CLI/MCP entry#874qiuguohua wants to merge 9 commits into
qiuguohua wants to merge 9 commits into
Conversation
bofeng-song
approved these changes
Aug 20, 2026
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.
PR cocos#872 added `if (node instanceof Scene)` in scene-process/service/component.ts after PR cocos#831 landed the test with a `jest.mock('cc', ...)` factory that had no Scene export. Both merged the same day, so the interaction only surfaced on main. Add Scene to the factory so the instanceof check resolves.
qiuguohua
force-pushed
the
fix/build-platform-param-validation
branch
from
August 20, 2026 10:55
6d3fa4d to
81d938d
Compare
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.
Changdw
approved these changes
Aug 21, 2026
bofeng-song
approved these changes
Aug 21, 2026
tangkaikk
reviewed
Aug 21, 2026
Contributor
There was a problem hiding this comment.
creator上next只有一个gles3渲染后端。需要核对下,为什么需要其他渲染后端?
Contributor
Author
There was a problem hiding this comment.
Harmonyos next一直在发展,vulkan和gles2 也是支持的,之前只是gles2之前有问题,vulkan没有验证过,暂时关闭了。next一直在发展,我感觉cli应该可以让用户配置,pink应该可以关闭配置。
Contributor
There was a problem hiding this comment.
CLI如果可以配置的话,也会导致用户选择后,构建出错。对于用户来说,排查成本可能比较高。不支持或者还未验证的东西,还是不要保留,以免留下用户报错后,不必要的问题排查的工作量。
Contributor
There was a problem hiding this comment.
如果确实要开放,就给测试,等测试验证没问题后,才放出去
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.
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.
What this PR does
The CLI/MCP build entry did not validate platform build options at all: an invalid value either blew up much later inside the native packaging step, or silently produced a wrong package. The editor validates the same options in three places (
verifyRuleMap,verificationFunc,getVerifyMap, plus panel-level checks). This PR adds a single validation gate at the CLI/MCP entry and migrates the editor rules that were missing on the CLI side into the platformverifyRuleMaps, which is the only validation entry the CLI has.1. Entry-layer validation
verifyBuildOptions(platform, options)runs beforebuild()inLauncher.build(CLI) andBuilderApi.build(MCP):PARAM_ERROR(32), with a per-field reason.skipCheck: truebypasses validation entirely.renderPipelineis resolved from the engine config. It is a project setting thatcheckProjectSettingonly fills inside the build task, so rules depending on it (apiLevelRenderPipeline) could never fire at the entry. This matches the editor, which reads the project profile directly.2. Rules migrated from the editor
targetVersion(format, TaskFlow minimum 12.0, general minimum 11.0),orientation,osTargetrenderBackEnd(was only checked in the editor UI layer)packageName,orientationpackageName(length 7..128 + reserved keyword tokens),appABIs,renderBackEnd,orientation,deviceTypesuseDebugKeystoreis off,apiLevelchain split into named rulesharmonyos-nextrenderBackEndnow exposesvulkan/gles3/gles2and declares an object-level default (gles3: true), so the value is no longerundefinedat build time.3. Bugs found while migrating
options.packages.android. Its own options live underoptions.packages['huawei-agc'], souseDebugKeystoreresolved toundefined, the keystore rules fired, and every huawei-agc build failed withPARAM_ERROR. All linked gates (useDebugKeystore,JobSystem,resizeableActivity,androidInstant) now resolve the package byoptions.platform.apiLevelTbbreadoptions.packages.native.JobSystem, a path that does not exist in the CLI (the CLI spreads the native common options into each platform package), so the rule never fired.'9.10' >= '11.0'; replaced with a segment-wise compare.validatorManagershort-circuited on empty values, which made conditional required rules unreachable. Empty values now also reach rules whose name ends withRequired.platformsdirectory, or nothing installed at or above the Google Play minimum of 24).4. Behaviour changes to be aware of
fixedValue, the error was downgraded to a warning and the build continued with the substituted value; the android/google-play hooks also bumpedapiLevelsilently. Both are gone.useDebugKeystore: falseand empty keystore fields are now blocked, even when no arguments are passed. Fix the profile or setuseDebugKeystore: true.32(PARAM_ERROR), not the generic build failure code.5. Tests
npx jest src/core/builder/test— 27 suites / 310 tests.platform-verify-rules.spec.ts— per-platform rule unit tests, plus huawei-agc end-to-end through the realvalidatorManager.verify-build-options.spec.ts— entry-layer unit tests withpluginManagerstubbed (default merge,skipCheck,PARAM_ERROR, warnings do not block).verify-build-options-integration.spec.ts— entry-layer integration tests that register the real android/ios configs into the realpluginManagerand drive the whole chain, including therenderPipelinesources.validator-manager-shortcircuit.spec.ts— the empty-value /Requiredsuffix convention.QA test guide
Setup
<PROJECT>.cocos-cli build -j <PROJECT> -p <platform> -c <config.json>. Only the keys you write are overridden; every other field falls back to its platform default, which is why some cases below have to switch off a default explicitly.$LASTEXITCODEecho $?Expected exit codes:
0= success,32= option validation rejected the build, other non-zero = build failure.Part A — invalid options must be rejected with exit code 32
For each case: write the JSON to
config.json, runcocos-cli build -j <PROJECT> -p <platform> -c config.json,then confirm exit code 32, that the printed reason names the field, and that no package was produced (
<PROJECT>/build/<platform>is not created or not updated — check the folder timestamp).{"packages":{"android":{"apiLevel":10}}}apiLevel,The minimum API Level required is 19.{"packages":{"android":{"apiLevel":"abc"}}}API Level must be a number{"packages":{"android":{"androidInstant":true,"apiLevel":22}}}the minimum API Level required is 23{"packages":{"android":{"packageName":"123abc"}}}Invalid package name specified{"packages":{"android":{"useDebugKeystore":false}}}keystorePath/keystorePassword/keystoreAlias/keystoreAliasPassword, each withCannot be empty when useDebugKeystore is false{"packages":{"android":{"renderBackEnd":{"vulkan":false,"gles3":false,"gles2":false}}}}renderBackEnd must have at least one supported backend enabled{"packages":{"android":{"orientation":{"portrait":false,"upsideDown":false,"landscapeLeft":false,"landscapeRight":false}}}}orientation must have at least one direction enabled{"packages":{"android":{"resizeableActivity":false,"maxAspectRatio":"1.0"}}}maxAspectRatio must be a number, "w:h", or "n.n (w:h)" with value >= 1.33{"packages":{"google-play":{"apiLevel":21}}}Google Play requires the minimum API Level to be 24.{"packages":{"windows":{"renderBackEnd":{"vulkan":false,"gles3":false,"gles2":false}}}}renderBackEnd must have at least one supported backend enabled{"packages":{"ios":{"packageName":"com.qa.test","targetVersion":"9.10"}}}The minimum target version required is 11.0.{"packages":{"ios":{"packageName":"com.qa.test","targetVersion":"12"}}}targetVersion must look like "12.0" or "12.0.1"{"packages":{"ios":{"packageName":"com.qa.test","osTarget":{"iphoneos":false,"simulator":false}}}}osTarget must have at least one target enabled{"packages":{"ohos":{"packageName":"123abc"}}}Invalid package name specified{"packages":{"harmonyos-next":{"packageName":"com.harmony.game"}}}Invalid package name specified(reserved keyword token){"packages":{"harmonyos-next":{"packageName":"com.qa"}}}Invalid package name specified(shorter than 7 characters){"packages":{"harmonyos-next":{"packageName":"com.qa.testgame","deviceTypes":{"default":false}}}}deviceTypes must have at least one device type enabledA18 — project-setting driven case, needs a project change: open
<PROJECT>in the editor, set the render pipeline to Deferred, then build android with{"packages":{"android":{"apiLevel":20}}}. Expected: exit code 32 andWhen Deferred Render Pipeline is enabled, the minimum API Level required is 21.Switch the pipeline back to Forward and the same command must succeed. This is the case that only works becauserenderPipelineis now read from the engine config at the entry.Part B — valid options must still build
{}{"packages":{"android":{"apiLevel":24}}}{"packages":{"android":{"useDebugKeystore":false,"keystorePath":"<a real .keystore>","keystorePassword":"...","keystoreAlias":"...","keystoreAliasPassword":"..."}}}{}{}{}CMakeLists/cMakeConfig containsCC_USE_GLES3=true, plusCC_USE_VULKAN=falseandCC_USE_GLES2=false(these two lines are new and expected){"packages":{"ios":{"packageName":"com.qa.testgame"}}}{}B9 — escape hatch:
{"skipCheck":true,"packages":{"android":{"apiLevel":10}}}must not be rejected by validation (exit code is whatever the build itself returns). Use this to confirm there is a way past the gate.Part C — regression checks on existing projects
useDebugKeystore: falsewith empty keystore fields, and runcocos-cli build -j <PROJECT> -p androidwith no config file. Expected: exit code 32 listing the four keystore fields. This is an intentional behaviour change — previously such a project would start building and fail later or produce an unsigned/incorrectly signed package. Filling the fields, or settinguseDebugKeystore: true, must make it build again.packageNameis required and its default is empty:cocos-cli build -j <PROJECT> -p ioswith no config must be rejected with apackageNamemessage. Passing a validpackageNamemust build.renderBackEndonly accepts backend keys the platform supports, so{"metal":true}on android is rejected by the CLI while the editor accepts it.Part D — MCP path
Call the
builder-buildtool with the same invalid options as A1 (for example via the MCP client: platformandroid, options{"packages":{"android":{"apiLevel":10}}}). Expected: the call returns a failure withcode: 32and areasonnamingapiLevel— the same gate, not an exception or a started build.Part E — google-play API Level list logging
In the google-play build panel, open the API Level dropdown and watch the console/log:
Android SDK path not found, cannot list available API Levels.$SDK/platformsonly has entries belowandroid-24: the dropdown is empty and a warning names the directory, lists what is installed, and says to install API Level 24 or above. (Easy to reproduce by pointing the SDK path at a copy of the directory with theandroid-24+folders removed.)What a failure looks like
If any Part A case builds instead of stopping, or any Part B case is rejected, that is a bug — please attach the config file, the platform, the printed reason, and the exit code.