-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Description
Hello,
I am trying to distribute a MAS app to the App Store Connect using Transporter, when I verify/ deliver the build I get this warning:
Cannot be used with TestFlight because the executable “${executable}” in bundle “${bundle}” is missing a provisioning profile but has an application identifier in its signature. Nested executables are expected to have provisioning profiles with application identifiers matching the identifier in the signature in order to be eligible for TestFlight." (90885)
The issue is that I’m not sure which executable is missing the provisioning profile, preventing me from adding it before packaging and uploading the app using Transporter.
I checked if I signed all the Electron apps using codesign
:
> codesign --verify --deep --strict --verbose=2 My\ App.app
--prepared:/Users/user/myapp/dist/mas-universal/My App.app/Contents/Frameworks/My App Helper (Plugin).app
--prepared:/Users/user/myapp/dist/mas-universal/My App.app/Contents/Frameworks/My App Helper.app
--prepared:/Users/user/myapp/dist/mas-universal/My App.app/Contents/Frameworks/My App Helper (Renderer).app
--validated:/Users/user/myapp/dist/mas-universal/My App.app/Contents/Frameworks/My App Helper (Plugin).app
--validated:/Users/user/myapp/dist/mas-universal/My App.app/Contents/Frameworks/My App Helper.app
--prepared:/Users/user/myapp/dist/mas-universal/My App.app/Contents/Library/LoginItems/My App Login Helper.app
--validated:/Users/user/myapp/dist/mas-universal/My App.app/Contents/Frameworks/My App Helper (Renderer).app
--prepared:/Users/user/myapp/dist/mas-universal/My App.app/Contents/Frameworks/My App Helper (GPU).app
--validated:/Users/user/myapp/dist/mas-universal/My App.app/Contents/Library/LoginItems/My App Login Helper.app
--validated:/Users/user/myapp/dist/mas-universal/My App.app/Contents/Frameworks/My App Helper (GPU).app
--prepared:/Users/user/myapp/dist/mas-universal/My App.app/Contents/Frameworks/Electron Framework.framework/Versions/Current/.
--validated:/Users/user/myapp/dist/mas-universal/My App.app/Contents/Frameworks/Electron Framework.framework/Versions/Current/.
My App.app: valid on disk
My App.app: satisfies its Designated Requirement
Also I checked if they have embedded provision profile:
> find My\ App.app -name embedded.provisionprofile
My App.app/Contents/Library/LoginItems/My App Login Helper.app/Contents/embedded.provisionprofile
My App.app/Contents/embedded.provisionprofile
My App.app/Contents/Frameworks/My App Helper (Plugin).app/Contents/embedded.provisionprofile
My App.app/Contents/Frameworks/My App Helper (Renderer).app/Contents/embedded.provisionprofile
My App.app/Contents/Frameworks/My App Helper (GPU).app/Contents/embedded.provisionprofile
My App.app/Contents/Frameworks/My App Helper.app/Contents/embedded.provisionprofile
And as you can see all Electron apps are signed and have an embedded provision profile, given my configuration below, has anyone faced a similar issue or know how can I resolve the Transporter error?
Terminal output:
> electron-builder --config electron-builder.cjs -m mas --universal
• electron-builder version=26.0.12 os=24.2.0
• loaded configuration file=/Users/user/myapp/electron-builder.cjs
• author is missed in the package.json appPackageFile=/Users/user/myapp/package.json
• writing effective config file=dist/builder-effective-config.yaml
• rebuilding native dependencies [email protected], [email protected], [email protected], [email protected] platform=darwin arch=x64
• install prebuilt binary name=sqlite3 version=5.1.7 platform=darwin arch=x64 napi=
34m• packaging platform=mas arch=x64 electron=35.7.4 appOutDir=dist/mas-universal-x64-temp
• rebuilding native dependencies [email protected], [email protected], [email protected], [email protected] platform=darwin arch=arm64
• install prebuilt binary name=sqlite3 version=5.1.7 platform=darwin arch=arm64 napi=
34m• packaging platform=mas arch=arm64 electron=35.7.4 appOutDir=dist/mas-universal-arm64-temp
• packaging platform=mas arch=universal electron=35.7.4 appOutDir=dist/mas-universal
• signing file=dist/mas-universal/My App.app platform=darwin type=distribution identityName=Developer ID Application: DELETED identityHash=DELETED provisioningProfile=none
• notarization successful
Embedding profile in Frameworks: /Users/user/myapp/dist/mas-universal/My App.app/Contents/Frameworks/My App Helper (GPU).app
Embedding profile in Frameworks: /Users/user/myapp/dist/mas-universal/My App.app/Contents/Frameworks/My App Helper (Plugin).app
Embedding profile in Frameworks: /Users/user/myapp/dist/mas-universal/My App.app/Contents/Frameworks/My App Helper (Renderer).app
Embedding profile in Frameworks: /Users/user/myapp/dist/mas-universal/My App.app/Contents/Frameworks/My App Helper.app
Embedding profile in Library: /Users/user/myapp/dist/mas-universal/My App.app/Contents/Library/LoginItems/My App Login Helper.app
• signing file=dist/mas-universal/My App.app platform=mas type=distribution identityName=Apple Distribution: DELETED identityHash=DELETED provisioningProfile=mas/myapp_desktop.provisionprofile
package.json
(some irrelevant packages have been removed):
{
"name": "my-app",
"version": "1.0.0",
"private": true,
"main": "build/main.js",
"description": "My App",
"scripts": {
"next:dev": "next dev --turbo",
"next:build1": "next build",
"next:build": "node scripts/next-build.mjs",
"next:start": "next start",
"postinstall": "electron-builder install-app-deps",
"electron:dist": "electron-builder --config electron-builder.cjs",
"electron:dist:mas-universal": "electron-builder --config electron-builder.cjs -m mas --universal",
"electron:dist:ci": "electron-builder --publish always --config electron-builder.cjs -mwl --x64 --arm64",
"electron:dist:deb": "electron-builder --linux deb",
"electron:build": "tsup",
"build": "run-s next:build electron:build",
"dist": "run-s build electron:dist",
"dist:mas-universal": "run-s build electron:dist:mas-universal"
},
"dependencies": {
"electron-context-menu": "^4.1.1",
"electron-log": "^5.3.4",
"electron-store": "^8.2.0",
"electron-updater": "^6.6.2",
"next": "^14.2.28"
},
"devDependencies": {
"electron": "^35.0.0",
"electron-builder": "^26.0.12",
"electron-devtools-installer": "^4.0.0",
"eslint": "^9.28.0",
"eslint-config-next": "^15.3.3",
"eslint-formatter-unix": "^8.40.0",
"eslint-import-resolver-typescript": "^4.4.3",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-storybook": "^0.11.1",
"eslint-plugin-tailwindcss": "^3.18.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"nodemon": "^3.1.4",
"npm-run-all": "^4.1.5",
"postcss": "^8.4.41",
"storybook": "^8.4.7",
"tailwindcss": "^3.4.7",
"ts-jest": "^29.4.1",
"ts-node": "^10.9.2",
"tsup": "^8.2.4",
"typescript": "^5.5.4",
"typescript-eslint": "^8.34.0"
},
"engines": {
"node": ">=22.0.0"
}
}
node -v
:
v22.17.0
npm -v
:
10.9.2
electron-builder config:
require('dotenv').config()
const buildTimestamp = Math.floor(Date.now() / 1000);
module.exports = {
icon: 'public/icon',
extraResources: [
'.env',
],
asar: true,
productName: process.env.APP_NAME || 'My App',
executableName: process.env.EXECUTABLE_NAME || 'My App',
appId: process.env.APP_ID,
asarUnpack: [
'node_modules/next',
'node_modules/@img',
'node_modules/sharp',
'**\\*.{node,dll}',
],
files: [
'build/**/*',
{
from: 'out',
to: 'out',
filter: ['**/*'],
},
],
mac: {
notarize: process.env.ENABLE_NOTARIZE || false,
identity: process.env.APPLE_IDENTITY,
target: ['dmg', 'zip', 'mas'],
bundleVersion: String(buildTimestamp)
},
mas: {
entitlements: "mas/entitlements.mas.plist",
entitlementsInherit: "mas/entitlements.mas.inherit.plist",
entitlementsLoginHelper: "mas/entitlements.mas.plist",
provisioningProfile: "mas/myapp_desktop.provisionprofile",
hardenedRuntime: true,
mergeASARs: false, // without this I get Pattern too long if I build the universal app
notarize: false,
gatekeeperAssess: true,
category: "public.app-category.productivity",
preAutoEntitlements: false, // without this I get validation error from Transporter saying that there are entitlements and profile mismatch
},
afterSign: "mas/afterSign.js",
nativeRebuilder: 'legacy',
}
entitlements.mas.plist
:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.application-identifier</key>
<!-- Real values omitted -->
<string>TEAMID.sa.com.myapp.example</string>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.app-sandbox</key>
<true/>
</dict>
</plist>
entitlements.mas.inherit.plist
:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.application-identifier</key>
<!-- Real values omitted -->
<string>TEAMID.sa.com.myapp.example</string>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.inherit</key>
<true/>
</dict>
</plist>
afterSign.js
(used to move the provision profile to each one of the electron executables: Helper GPU, Helper Renderer, etc.):
// mas/afterSign.js
const fs = require('fs')
const path = require('path')
exports.default = async function(context) {
const { appOutDir, packager } = context
const appName = packager.appInfo.productFilename
const appDir = path.join(appOutDir, `${appName}.app`)
const frameworksDir = path.join(appDir, 'Contents/Frameworks')
const libraryDir = path.join(appDir, 'Contents/Library')
const masProvisioningProfile = 'mas/myapp_desktop.provisionprofile'
if (!fs.existsSync(masProvisioningProfile)) {
console.error(`Provisioning profile not found at ${masProvisioningProfile}`)
return
}
function findAppBundlesRecursively(baseDir) {
const foundApps = []
function walk(currentDir) {
if (!fs.existsSync(currentDir)) return
const entries = fs.readdirSync(currentDir, { withFileTypes: true })
for (const entry of entries) {
const fullPath = path.join(currentDir, entry.name)
if (entry.isDirectory()) {
if (entry.name.endsWith('.app')) {
foundApps.push(fullPath)
} else {
walk(fullPath) // recurse deeper
}
}
}
}
walk(baseDir)
return foundApps
}
function embedProfileInApps(appDirs, locationName) {
if (!appDirs.length) {
console.log(`No .app bundles found in ${locationName}, skipping.`)
return
}
for (const helperAppDir of appDirs) {
const dest = path.join(helperAppDir, 'Contents/embedded.provisionprofile')
console.log(`Embedding profile in ${locationName}: ${helperAppDir}`)
fs.copyFileSync(masProvisioningProfile, dest)
}
}
// 1. Embed in Frameworks helper apps (shallow search)
if (fs.existsSync(frameworksDir)) {
const frameworkApps = fs
.readdirSync(frameworksDir)
.filter((d) => d.endsWith('.app'))
.map((d) => path.join(frameworksDir, d))
embedProfileInApps(frameworkApps, 'Frameworks')
} else {
console.log('No Frameworks directory found, skipping.')
}
// 2. Embed in *any* helper apps under Library (recursive)
if (fs.existsSync(libraryDir)) {
const libraryApps = findAppBundlesRecursively(libraryDir)
embedProfileInApps(libraryApps, 'Library')
} else {
console.log('No Library directory found, skipping.')
}
}