Skip to content

feature: updated entitlement/capabilities parsing from export_presets.cfg and sync with Apple portal#164

Merged
madebydavid merged 6 commits intomainfrom
163-feature-support-the-additional-entitlement-capabilities
Feb 28, 2026
Merged

feature: updated entitlement/capabilities parsing from export_presets.cfg and sync with Apple portal#164
madebydavid merged 6 commits intomainfrom
163-feature-support-the-additional-entitlement-capabilities

Conversation

@madebydavid
Copy link
Member

This is to resolve #163

  • Reviewed the godot source code and collated list of capabilities and entitlements needing to be synced
  • Upgraded godot-export-presets to handle issue parsing file with "@" in key
  • Added full list of Apple capabilities
  • Correctly mapping capability/entitlement in export presets to "syncable" capabilities which we can set in Apple
  • Handling the entitlements/additional XML parsing
  • Added tests for mapping capabilities and parsing the XML in entitlements/additional

@madebydavid madebydavid linked an issue Feb 28, 2026 that may be closed by this pull request
5 tasks
@madebydavid madebydavid requested a review from Copilot February 28, 2026 17:01
@madebydavid madebydavid marked this pull request as ready for review February 28, 2026 17:02
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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_CAPABILITY mapping + parseEntitlementsAdditional helper to translate raw entitlement XML into CapabilityTypes.
  • Added unit tests for capability mapping and entitlement XML parsing; bumped godot-export-presets dependency.

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.

Comment on lines +52 to +60
export const GODOT_CAPABILITIES: GodotSyncableCapability[] = [
...GODOT_SYNCABLE_CAPABILITIES,
...Object.entries(ENTITLEMENT_KEY_TO_CAPABILITY).map(([key, {name, type}]) => ({
key: `entitlements/additional (${key})`,
name,
type,
})),
]

Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
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]
})()

Copilot uses AI. Check for mistakes.
Comment on lines +76 to +82
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)) {
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
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)) {

Copilot uses AI. Check for mistakes.
Comment on lines +52 to +58
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)
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

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

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).

Copilot uses AI. Check for mistakes.
@madebydavid
Copy link
Member Author

I have updated with changes from the @copilot code review[agent] review

Also changed the shipthis game ios profile create command so that it uses the current date and time in the profile name. If someone has created a profile before the entitlements are correctly synced, they will need to remake it - doing so would fail because the name was the same.

Also updated the docs

Copy link
Contributor

Copilot AI commented Feb 28, 2026

@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.

@madebydavid madebydavid merged commit db9efb1 into main Feb 28, 2026
@madebydavid madebydavid deleted the 163-feature-support-the-additional-entitlement-capabilities branch February 28, 2026 18:11
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.

feature: support the additional entitlement / capabilities

3 participants