feature: updated entitlement/capabilities parsing from export_presets.cfg and sync with Apple portal#164
Conversation
…sing lib to handle error.
There was a problem hiding this comment.
Pull request overview
Updates ShipThis’ Godot iOS export preset parsing so entitlements/capabilities (including entitlements/additional) map correctly to Apple Developer Portal “syncable” Bundle ID capabilities, addressing issue #163.
Changes:
- Expanded capability/entitlement detection from
export_presets.cfg, including push notification legacy+new keys and additional entitlements parsing. - Added
ENTITLEMENT_KEY_TO_CAPABILITYmapping +parseEntitlementsAdditionalhelper to translate raw entitlement XML intoCapabilityTypes. - Added unit tests for capability mapping and entitlement XML parsing; bumped
godot-export-presetsdependency.
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
src/utils/godot.ts |
Updates Godot iOS capability detection, adds “syncable vs display-only” separation, and incorporates additional entitlement parsing. |
src/apple/entitlements.ts |
Introduces entitlement-key → capability mapping and a helper to detect known keys inside raw entitlements XML. |
test/utils/godot.test.ts |
Adds tests for push key handling, new entitlements, and entitlements/additional influence on capabilities. |
test/apple/entitlements.test.ts |
Adds direct tests for the entitlement mapping table and parseEntitlementsAdditional. |
package.json / package-lock.json |
Upgrades godot-export-presets to ^0.1.9. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export const GODOT_CAPABILITIES: GodotSyncableCapability[] = [ | ||
| ...GODOT_SYNCABLE_CAPABILITIES, | ||
| ...Object.entries(ENTITLEMENT_KEY_TO_CAPABILITY).map(([key, {name, type}]) => ({ | ||
| key: `entitlements/additional (${key})`, | ||
| name, | ||
| type, | ||
| })), | ||
| ] | ||
|
|
There was a problem hiding this comment.
GODOT_CAPABILITIES is built by concatenating GODOT_SYNCABLE_CAPABILITIES with all entries from ENTITLEMENT_KEY_TO_CAPABILITY. Since ENTITLEMENT_KEY_TO_CAPABILITY includes com.apple.developer.game-center (type CapabilityType.GAME_CENTER) and GODOT_SYNCABLE_CAPABILITIES also has a GAME_CENTER entry, the Bundle ID capabilities table will end up with duplicate rows for the same capability type. Consider de-duplicating GODOT_CAPABILITIES by type (e.g., build via a Map keyed by type, or filter additional entries whose type already exists in the fixed list) so the UI/table output stays unique per capability.
| export const GODOT_CAPABILITIES: GodotSyncableCapability[] = [ | |
| ...GODOT_SYNCABLE_CAPABILITIES, | |
| ...Object.entries(ENTITLEMENT_KEY_TO_CAPABILITY).map(([key, {name, type}]) => ({ | |
| key: `entitlements/additional (${key})`, | |
| name, | |
| type, | |
| })), | |
| ] | |
| export const GODOT_CAPABILITIES: GodotSyncableCapability[] = (() => { | |
| const existingTypes = new Set(GODOT_SYNCABLE_CAPABILITIES.map((cap) => cap.type)) | |
| const additional: GodotSyncableCapability[] = [] | |
| for (const [key, {name, type}] of Object.entries(ENTITLEMENT_KEY_TO_CAPABILITY)) { | |
| if (existingTypes.has(type)) continue | |
| additional.push({ | |
| key: `entitlements/additional (${key})`, | |
| name, | |
| type, | |
| }) | |
| } | |
| return [...GODOT_SYNCABLE_CAPABILITIES, ...additional] | |
| })() |
src/apple/entitlements.ts
Outdated
| export function parseEntitlementsAdditional( | ||
| raw: string, | ||
| ): (typeof CapabilityType)[keyof typeof CapabilityType][] { | ||
| const types: (typeof CapabilityType)[keyof typeof CapabilityType][] = [] | ||
| if (!raw || typeof raw !== 'string') return types | ||
| for (const entitlementKey of Object.keys(ENTITLEMENT_KEY_TO_CAPABILITY)) { | ||
| if (raw.includes(entitlementKey)) { |
There was a problem hiding this comment.
parseEntitlementsAdditional uses raw.includes(entitlementKey) which can produce false positives when one known key is a substring of another. In the current map, com.apple.developer.healthkit is a prefix of com.apple.developer.healthkit.recalibrate-estimates, so a raw string containing only the latter will match both and return both CapabilityTypes. Consider matching exact <key>…</key> tokens (e.g., via a regex for <key>\s*${key}\s*</key>) or parsing the plist/XML to extract keys before mapping.
| export function parseEntitlementsAdditional( | |
| raw: string, | |
| ): (typeof CapabilityType)[keyof typeof CapabilityType][] { | |
| const types: (typeof CapabilityType)[keyof typeof CapabilityType][] = [] | |
| if (!raw || typeof raw !== 'string') return types | |
| for (const entitlementKey of Object.keys(ENTITLEMENT_KEY_TO_CAPABILITY)) { | |
| if (raw.includes(entitlementKey)) { | |
| function escapeForRegExp(value: string): string { | |
| return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') | |
| } | |
| function rawHasEntitlementKey(raw: string, entitlementKey: string): boolean { | |
| // Match XML/plist key tokens like: <key> com.apple.developer.healthkit </key> | |
| const pattern = new RegExp(`<key>\\s*${escapeForRegExp(entitlementKey)}\\s*</key>`) | |
| return pattern.test(raw) | |
| } | |
| export function parseEntitlementsAdditional( | |
| raw: string, | |
| ): (typeof CapabilityType)[keyof typeof CapabilityType][] { | |
| const types: (typeof CapabilityType)[keyof typeof CapabilityType][] = [] | |
| if (!raw || typeof raw !== 'string') return types | |
| for (const entitlementKey of Object.keys(ENTITLEMENT_KEY_TO_CAPABILITY)) { | |
| if (rawHasEntitlementKey(raw, entitlementKey)) { |
| it('returns multiple types when multiple known keys present', () => { | ||
| const raw = | ||
| '<key>com.apple.developer.healthkit</key><true/><key>com.apple.developer.homekit</key><true/>' | ||
| const result = parseEntitlementsAdditional(raw) | ||
| expect(result).to.include(CapabilityType.HEALTH_KIT) | ||
| expect(result).to.include(CapabilityType.HOME_KIT) | ||
| expect(result).to.have.lengthOf(2) |
There was a problem hiding this comment.
The parseEntitlementsAdditional tests don't currently cover the substring-collision case introduced by keys like com.apple.developer.healthkit vs com.apple.developer.healthkit.recalibrate-estimates. Adding a regression test for this scenario would help ensure the parser only matches exact entitlement keys (and prevent accidental double-matches when a longer key contains a shorter one).
|
I have updated with changes from the @copilot code review[agent] review Also changed the Also updated the docs |
|
@madebydavid I've opened a new pull request, #165, to work on those changes. Once the pull request is ready, I'll request review from you. |
This is to resolve #163