mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-09 15:59:30 +00:00
fix(ios): wire share extension app group signing
This commit is contained in:
parent
2983edd5a2
commit
f7f415f26b
20 changed files with 137 additions and 8 deletions
|
|
@ -3,6 +3,7 @@
|
|||
"signingRepo": "git@github.com:openclaw/apps-signing.git",
|
||||
"signingBranch": "main",
|
||||
"profileType": "appstore",
|
||||
"appGroupId": "group.ai.openclawfoundation.app.shared",
|
||||
"targets": [
|
||||
{
|
||||
"target": "OpenClaw",
|
||||
|
|
@ -11,7 +12,8 @@
|
|||
"platform": "IOS",
|
||||
"profileKey": "OPENCLAW_APP_PROFILE",
|
||||
"profileName": "OpenClaw App Store ai.openclawfoundation.app",
|
||||
"capabilities": ["PUSH_NOTIFICATIONS"]
|
||||
"capabilities": ["PUSH_NOTIFICATIONS", "APP_GROUPS"],
|
||||
"appGroups": ["group.ai.openclawfoundation.app.shared"]
|
||||
},
|
||||
{
|
||||
"target": "OpenClawShareExtension",
|
||||
|
|
@ -20,7 +22,8 @@
|
|||
"platform": "IOS",
|
||||
"profileKey": "OPENCLAW_SHARE_PROFILE",
|
||||
"profileName": "OpenClaw App Store ai.openclawfoundation.app.share",
|
||||
"capabilities": []
|
||||
"capabilities": ["APP_GROUPS"],
|
||||
"appGroups": ["group.ai.openclawfoundation.app.shared"]
|
||||
},
|
||||
{
|
||||
"target": "OpenClawActivityWidget",
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ OPENCLAW_DEVELOPMENT_TEAM = $(OPENCLAW_IOS_SELECTED_TEAM)
|
|||
OPENCLAW_CODE_SIGN_STYLE = Automatic
|
||||
OPENCLAW_CODE_SIGN_IDENTITY = Apple Development
|
||||
OPENCLAW_APP_BUNDLE_ID = ai.openclawfoundation.app
|
||||
OPENCLAW_APP_GROUP_ID = group.ai.openclawfoundation.app.shared
|
||||
OPENCLAW_WATCH_APP_BUNDLE_ID = ai.openclawfoundation.app.watchkitapp
|
||||
OPENCLAW_ACTIVITY_WIDGET_BUNDLE_ID = ai.openclawfoundation.app.activitywidget
|
||||
OPENCLAW_ACTIVITY_WIDGET_PROFILE =
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ OPENCLAW_DEVELOPMENT_TEAM = YOUR_TEAM_ID
|
|||
|
||||
OPENCLAW_APP_BUNDLE_ID = ai.openclawfoundation.app
|
||||
OPENCLAW_SHARE_BUNDLE_ID = ai.openclawfoundation.app.share
|
||||
OPENCLAW_APP_GROUP_ID = group.ai.openclawfoundation.app.shared
|
||||
OPENCLAW_ACTIVITY_WIDGET_BUNDLE_ID = ai.openclawfoundation.app.activitywidget
|
||||
OPENCLAW_WATCH_APP_BUNDLE_ID = ai.openclawfoundation.app.watchkitapp
|
||||
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ Release-owner secrets:
|
|||
|
||||
- App Store Connect API auth uses Keychain for private key material plus non-secret `apps/ios/fastlane/.env` variables.
|
||||
- The encrypted signing repo password lives outside this repo in the release-owner vault and is exposed locally as `MATCH_PASSWORD`.
|
||||
- The share sheet requires the Apple Developer App Group in `apps/ios/Config/AppStoreSigning.json` to be associated with both the app and share-extension bundle IDs before App Store profiles are regenerated.
|
||||
- Apple Distribution private keys, certificates, provisioning profiles, and decrypted signing sync output stay under `apps/ios/build/` or Keychain and are gitignored.
|
||||
- Rotating release signing means refreshing Fastlane `match` assets and pushing a fresh encrypted sync state.
|
||||
|
||||
|
|
@ -156,6 +157,8 @@ This should create `apps/ios/fastlane/.env` with non-secret App Store Connect va
|
|||
- `ai.openclawfoundation.app.activitywidget`
|
||||
- `ai.openclawfoundation.app.watchkitapp`
|
||||
|
||||
The main app and share extension must both be associated with the App Group pinned in `apps/ios/Config/AppStoreSigning.json`.
|
||||
|
||||
Use `pnpm ios:release:signing:setup` for the initial portal setup, then `MATCH_PASSWORD=... pnpm ios:release:signing:sync:push` to publish encrypted Fastlane match assets to the shared private repo.
|
||||
|
||||
4. Optional: set a custom official relay URL for the build. If unset, the release flow uses `https://ios-push-relay.openclaw.ai`.
|
||||
|
|
|
|||
|
|
@ -41,5 +41,7 @@
|
|||
<key>NSExtensionPrincipalClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).ShareViewController</string>
|
||||
</dict>
|
||||
<key>OpenClawAppGroupIdentifier</key>
|
||||
<string>$(OPENCLAW_APP_GROUP_ID)</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
10
apps/ios/ShareExtension/OpenClawShareExtension.entitlements
Normal file
10
apps/ios/ShareExtension/OpenClawShareExtension.entitlements
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?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.security.application-groups</key>
|
||||
<array>
|
||||
<string>$(OPENCLAW_APP_GROUP_ID)</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -10,6 +10,7 @@ OPENCLAW_DEVELOPMENT_TEAM = FWJYW4S8P8
|
|||
|
||||
OPENCLAW_APP_BUNDLE_ID = ai.openclawfoundation.app
|
||||
OPENCLAW_SHARE_BUNDLE_ID = ai.openclawfoundation.app.share
|
||||
OPENCLAW_APP_GROUP_ID = group.ai.openclawfoundation.app.shared
|
||||
OPENCLAW_WATCH_APP_BUNDLE_ID = ai.openclawfoundation.app.watchkitapp
|
||||
OPENCLAW_ACTIVITY_WIDGET_BUNDLE_ID = ai.openclawfoundation.app.activitywidget
|
||||
OPENCLAW_APNS_ENTITLEMENT_ENVIRONMENT = development
|
||||
|
|
|
|||
|
|
@ -78,6 +78,8 @@
|
|||
<string>OpenClaw uses on-device speech recognition for talk mode and voice wake.</string>
|
||||
<key>NSSupportsLiveActivities</key>
|
||||
<true/>
|
||||
<key>OpenClawAppGroupIdentifier</key>
|
||||
<string>$(OPENCLAW_APP_GROUP_ID)</string>
|
||||
<key>OpenClawCanonicalVersion</key>
|
||||
<string>$(OPENCLAW_IOS_VERSION)</string>
|
||||
<key>OpenClawPushAPNsEnvironment</key>
|
||||
|
|
|
|||
|
|
@ -4,5 +4,9 @@
|
|||
<dict>
|
||||
<key>aps-environment</key>
|
||||
<string>$(OPENCLAW_APNS_ENTITLEMENT_ENVIRONMENT)</string>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>$(OPENCLAW_APP_GROUP_ID)</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@ import OpenClawKit
|
|||
import Testing
|
||||
|
||||
@Suite struct ShareToAgentDeepLinkTests {
|
||||
@Test func appGroupIdentifierUsesCanonicalOpenClawGroup() {
|
||||
#expect(OpenClawAppGroup.canonicalIdentifier == "group.ai.openclawfoundation.app.shared")
|
||||
}
|
||||
|
||||
@Test func buildMessageIncludesSharedFields() {
|
||||
let payload = SharedContentPayload(
|
||||
title: "Article",
|
||||
|
|
|
|||
|
|
@ -492,6 +492,9 @@ def produce_services_for_target(target)
|
|||
if target.fetch("capabilities").include?("PUSH_NOTIFICATIONS")
|
||||
services[:push_notification] = "on"
|
||||
end
|
||||
if target.fetch("capabilities").include?("APP_GROUPS")
|
||||
services[:app_group] = "on"
|
||||
end
|
||||
services
|
||||
end
|
||||
|
||||
|
|
@ -567,6 +570,15 @@ def profile_plist_value(profile_path, key_path)
|
|||
end
|
||||
end
|
||||
|
||||
def profile_plist_array_values(profile_path, key_path)
|
||||
raw = profile_plist_value(profile_path, key_path)
|
||||
return [] unless raw
|
||||
|
||||
raw.lines.map(&:strip).reject do |line|
|
||||
line.empty? || line == "Array {" || line == "}"
|
||||
end
|
||||
end
|
||||
|
||||
def validate_match_profile_capabilities!(target)
|
||||
capabilities = target.fetch("capabilities")
|
||||
return if capabilities.empty?
|
||||
|
|
@ -582,6 +594,17 @@ def validate_match_profile_capabilities!(target)
|
|||
)
|
||||
end
|
||||
end
|
||||
|
||||
if capabilities.include?("APP_GROUPS")
|
||||
expected_app_groups = target.fetch("appGroups")
|
||||
actual_app_groups = profile_plist_array_values(profile_path, "Entitlements:com.apple.security.application-groups")
|
||||
missing = expected_app_groups - actual_app_groups
|
||||
unless missing.empty?
|
||||
UI.user_error!(
|
||||
"Provisioning profile #{target.fetch("profileName")} for #{target.fetch("bundleId")} is missing App Groups #{missing.join(", ")}; actual groups: #{actual_app_groups.empty? ? "missing" : actual_app_groups.join(", ")}."
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def sync_app_store_signing!(readonly:)
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ pnpm ios:release:signing:check
|
|||
pnpm ios:release:signing:setup
|
||||
```
|
||||
|
||||
`signing:setup` uses Fastlane `produce` and `modify_services` to create Developer Portal bundle IDs and enable required services before running `match`. If Fastlane does not already have a valid Apple Developer Portal session, run `fastlane spaceauth` for a release-owner Apple ID and export the resulting `FASTLANE_SESSION`.
|
||||
`signing:setup` uses Fastlane `produce` and `modify_services` to create Developer Portal bundle IDs and enable required services before running `match`. The main app and share extension also require the shared App Group from `apps/ios/Config/AppStoreSigning.json`; associate that group with both bundle IDs in the Apple Developer Portal before regenerating profiles. If Fastlane does not already have a valid Apple Developer Portal session, run `fastlane spaceauth` for a release-owner Apple ID and export the resulting `FASTLANE_SESSION`.
|
||||
|
||||
Shared encrypted signing storage:
|
||||
|
||||
|
|
|
|||
|
|
@ -142,6 +142,7 @@ targets:
|
|||
- openclaw
|
||||
CFBundleShortVersionString: "$(OPENCLAW_MARKETING_VERSION)"
|
||||
OpenClawCanonicalVersion: "$(OPENCLAW_IOS_VERSION)"
|
||||
OpenClawAppGroupIdentifier: "$(OPENCLAW_APP_GROUP_ID)"
|
||||
CFBundleVersion: "$(OPENCLAW_BUILD_VERSION)"
|
||||
UILaunchScreen: {}
|
||||
UIApplicationSceneManifest:
|
||||
|
|
@ -194,6 +195,7 @@ targets:
|
|||
settings:
|
||||
base:
|
||||
CODE_SIGN_IDENTITY: "$(OPENCLAW_CODE_SIGN_IDENTITY)"
|
||||
CODE_SIGN_ENTITLEMENTS: ShareExtension/OpenClawShareExtension.entitlements
|
||||
CODE_SIGN_STYLE: "$(OPENCLAW_CODE_SIGN_STYLE)"
|
||||
DEVELOPMENT_TEAM: "$(OPENCLAW_DEVELOPMENT_TEAM)"
|
||||
ENABLE_APPINTENTS_METADATA: NO
|
||||
|
|
@ -208,6 +210,7 @@ targets:
|
|||
properties:
|
||||
CFBundleDisplayName: OpenClaw Share
|
||||
CFBundleShortVersionString: "$(OPENCLAW_MARKETING_VERSION)"
|
||||
OpenClawAppGroupIdentifier: "$(OPENCLAW_APP_GROUP_ID)"
|
||||
CFBundleVersion: "$(OPENCLAW_BUILD_VERSION)"
|
||||
NSExtension:
|
||||
NSExtensionPointIdentifier: com.apple.share-services
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
import Foundation
|
||||
|
||||
public enum OpenClawAppGroup {
|
||||
public static let canonicalIdentifier = "group.ai.openclawfoundation.app.shared"
|
||||
|
||||
public static var identifier: String {
|
||||
let raw = Bundle.main.object(forInfoDictionaryKey: "OpenClawAppGroupIdentifier") as? String
|
||||
let trimmed = raw?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
return trimmed.isEmpty ? self.canonicalIdentifier : trimmed
|
||||
}
|
||||
}
|
||||
|
|
@ -26,7 +26,7 @@ public struct ShareGatewayRelayConfig: Codable, Sendable, Equatable {
|
|||
}
|
||||
|
||||
public enum ShareGatewayRelaySettings {
|
||||
private static let suiteName = "group.ai.openclaw.shared"
|
||||
private static var suiteName: String { OpenClawAppGroup.identifier }
|
||||
private static let relayConfigKey = "share.gatewayRelay.config.v1"
|
||||
private static let lastEventKey = "share.gatewayRelay.event.v1"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import Foundation
|
||||
|
||||
public enum ShareToAgentSettings {
|
||||
private static let suiteName = "group.ai.openclaw.shared"
|
||||
private static var suiteName: String { OpenClawAppGroup.identifier }
|
||||
private static let defaultInstructionKey = "share.defaultInstruction"
|
||||
|
||||
private static var defaults: UserDefaults {
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ fi
|
|||
bundle_base="$(normalize_bundle_id "${bundle_base}")"
|
||||
|
||||
share_bundle_id="${OPENCLAW_IOS_SHARE_BUNDLE_ID:-${bundle_base}.share}"
|
||||
app_group_id="${OPENCLAW_IOS_APP_GROUP_ID:-group.${bundle_base}.shared}"
|
||||
activity_widget_bundle_id="${OPENCLAW_IOS_ACTIVITY_WIDGET_BUNDLE_ID:-${bundle_base}.activitywidget}"
|
||||
watch_app_bundle_id="${OPENCLAW_IOS_WATCH_APP_BUNDLE_ID:-${bundle_base}.watchkitapp}"
|
||||
|
||||
|
|
@ -86,7 +87,8 @@ cat >"${tmp_file}" <<EOF
|
|||
// Override values with env vars if needed:
|
||||
// IOS_DEVELOPMENT_TEAM / IOS_PREFERRED_TEAM_ID
|
||||
// OPENCLAW_IOS_APP_BUNDLE_ID / OPENCLAW_IOS_BUNDLE_ID_BASE
|
||||
// OPENCLAW_IOS_SHARE_BUNDLE_ID / OPENCLAW_IOS_ACTIVITY_WIDGET_BUNDLE_ID
|
||||
// OPENCLAW_IOS_SHARE_BUNDLE_ID / OPENCLAW_IOS_APP_GROUP_ID
|
||||
// OPENCLAW_IOS_ACTIVITY_WIDGET_BUNDLE_ID
|
||||
// OPENCLAW_IOS_WATCH_APP_BUNDLE_ID
|
||||
// OPENCLAW_IOS_CODE_SIGN_STYLE / OPENCLAW_IOS_CODE_SIGN_IDENTITY
|
||||
// OPENCLAW_IOS_APNS_ENTITLEMENT_ENVIRONMENT / OPENCLAW_IOS_APP_PROFILE
|
||||
|
|
@ -99,6 +101,7 @@ OPENCLAW_DEVELOPMENT_TEAM = ${team_id}
|
|||
OPENCLAW_IOS_SELECTED_TEAM = ${team_id}
|
||||
OPENCLAW_APP_BUNDLE_ID = ${bundle_base}
|
||||
OPENCLAW_SHARE_BUNDLE_ID = ${share_bundle_id}
|
||||
OPENCLAW_APP_GROUP_ID = ${app_group_id}
|
||||
OPENCLAW_ACTIVITY_WIDGET_BUNDLE_ID = ${activity_widget_bundle_id}
|
||||
OPENCLAW_WATCH_APP_BUNDLE_ID = ${watch_app_bundle_id}
|
||||
OPENCLAW_APNS_ENTITLEMENT_ENVIRONMENT = ${apns_entitlement_environment}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,15 @@ import { fileURLToPath } from "node:url";
|
|||
const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const defaultManifestPath = path.join(rootDir, "apps", "ios", "Config", "AppStoreSigning.json");
|
||||
|
||||
function validateAppGroupId(value, context) {
|
||||
if (typeof value !== "string" || value.trim() === "") {
|
||||
throw new Error(`${context} must be a non-empty string.`);
|
||||
}
|
||||
if (!/^group\.[A-Za-z0-9.-]+$/.test(value)) {
|
||||
throw new Error(`${context} must be an Apple app group identifier beginning with group.`);
|
||||
}
|
||||
}
|
||||
|
||||
function usage() {
|
||||
process.stdout.write(`Usage:
|
||||
scripts/ios-release-signing.mjs --mode plan
|
||||
|
|
@ -59,6 +68,9 @@ function readManifest(manifestPath) {
|
|||
if (!Array.isArray(parsed.targets) || parsed.targets.length === 0) {
|
||||
throw new Error("Signing manifest must include targets.");
|
||||
}
|
||||
if (typeof parsed.appGroupId !== "undefined") {
|
||||
validateAppGroupId(parsed.appGroupId, "Signing manifest appGroupId");
|
||||
}
|
||||
|
||||
for (const target of parsed.targets) {
|
||||
for (const key of [
|
||||
|
|
@ -76,6 +88,40 @@ function readManifest(manifestPath) {
|
|||
if (!Array.isArray(target.capabilities)) {
|
||||
throw new Error(`Signing target ${target.target} must include capabilities array.`);
|
||||
}
|
||||
for (const capability of target.capabilities) {
|
||||
if (typeof capability !== "string" || capability.trim() === "") {
|
||||
throw new Error(`Signing target ${target.target} capabilities must be non-empty strings.`);
|
||||
}
|
||||
}
|
||||
|
||||
const appGroups = target.appGroups ?? [];
|
||||
if (!Array.isArray(appGroups)) {
|
||||
throw new Error(`Signing target ${target.target} appGroups must be an array when present.`);
|
||||
}
|
||||
for (const appGroup of appGroups) {
|
||||
validateAppGroupId(appGroup, `Signing target ${target.target} appGroups entry`);
|
||||
}
|
||||
|
||||
const hasAppGroupsCapability = target.capabilities.includes("APP_GROUPS");
|
||||
if (hasAppGroupsCapability && appGroups.length === 0) {
|
||||
throw new Error(
|
||||
`Signing target ${target.target} must list appGroups when APP_GROUPS is enabled.`,
|
||||
);
|
||||
}
|
||||
if (!hasAppGroupsCapability && appGroups.length > 0) {
|
||||
throw new Error(
|
||||
`Signing target ${target.target} lists appGroups without APP_GROUPS capability.`,
|
||||
);
|
||||
}
|
||||
if (
|
||||
typeof parsed.appGroupId === "string" &&
|
||||
appGroups.length > 0 &&
|
||||
!appGroups.includes(parsed.appGroupId)
|
||||
) {
|
||||
throw new Error(
|
||||
`Signing target ${target.target} appGroups must include manifest appGroupId.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return parsed;
|
||||
|
|
@ -86,6 +132,9 @@ function writeXcconfig(manifest) {
|
|||
"OPENCLAW_CODE_SIGN_STYLE = Manual",
|
||||
"OPENCLAW_CODE_SIGN_IDENTITY = Apple Distribution",
|
||||
];
|
||||
if (typeof manifest.appGroupId === "string") {
|
||||
lines.push(`OPENCLAW_APP_GROUP_ID = ${manifest.appGroupId}`);
|
||||
}
|
||||
|
||||
for (const target of manifest.targets) {
|
||||
lines.push(`${target.profileKey} = ${target.profileName}`);
|
||||
|
|
@ -106,8 +155,10 @@ Targets:
|
|||
`);
|
||||
for (const target of manifest.targets) {
|
||||
const capabilities = target.capabilities.length > 0 ? target.capabilities.join(", ") : "none";
|
||||
const appGroups =
|
||||
target.appGroups?.length > 0 ? `, app groups: ${target.appGroups.join(", ")}` : "";
|
||||
process.stdout.write(
|
||||
`- ${target.target}: ${target.bundleId}, profile "${target.profileName}", capabilities: ${capabilities}\n`,
|
||||
`- ${target.target}: ${target.bundleId}, profile "${target.profileName}", capabilities: ${capabilities}${appGroups}\n`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ function runConfigureSigning(teamId: string, user = "localuser"): string {
|
|||
OPENCLAW_IOS_APP_BUNDLE_ID: "",
|
||||
OPENCLAW_IOS_BUNDLE_ID_BASE: "",
|
||||
OPENCLAW_IOS_BUNDLE_SUFFIX: "",
|
||||
OPENCLAW_IOS_APP_GROUP_ID: "",
|
||||
OPENCLAW_IOS_SHARE_BUNDLE_ID: "",
|
||||
OPENCLAW_IOS_ACTIVITY_WIDGET_BUNDLE_ID: "",
|
||||
OPENCLAW_IOS_WATCH_APP_BUNDLE_ID: "",
|
||||
|
|
@ -65,6 +66,7 @@ describe.sequential("scripts/ios-configure-signing.sh", () => {
|
|||
expect(generated).toContain("OPENCLAW_DEVELOPMENT_TEAM = FWJYW4S8P8");
|
||||
expect(generated).toContain("OPENCLAW_APP_BUNDLE_ID = ai.openclawfoundation.app");
|
||||
expect(generated).toContain("OPENCLAW_SHARE_BUNDLE_ID = ai.openclawfoundation.app.share");
|
||||
expect(generated).toContain("OPENCLAW_APP_GROUP_ID = group.ai.openclawfoundation.app.shared");
|
||||
expect(generated).toContain("OPENCLAW_ACTIVITY_WIDGET_PROFILE = ");
|
||||
});
|
||||
|
||||
|
|
@ -79,5 +81,8 @@ describe.sequential("scripts/ios-configure-signing.sh", () => {
|
|||
expect(generated).toContain(
|
||||
"OPENCLAW_APP_BUNDLE_ID = ai.openclawfoundation.app.test.localuser-y3yuzp442g",
|
||||
);
|
||||
expect(generated).toContain(
|
||||
"OPENCLAW_APP_GROUP_ID = group.ai.openclawfoundation.app.test.localuser-y3yuzp442g.shared",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ describe("scripts/ios-release-signing.mjs", () => {
|
|||
|
||||
expect(output).toContain("OPENCLAW_CODE_SIGN_STYLE = Manual");
|
||||
expect(output).toContain("OPENCLAW_CODE_SIGN_IDENTITY = Apple Distribution");
|
||||
expect(output).toContain("OPENCLAW_APP_GROUP_ID = group.ai.openclawfoundation.app.shared");
|
||||
expect(output).toContain("OPENCLAW_APP_PROFILE = OpenClaw App Store ai.openclawfoundation.app");
|
||||
expect(output).toContain(
|
||||
"OPENCLAW_SHARE_PROFILE = OpenClaw App Store ai.openclawfoundation.app.share",
|
||||
|
|
@ -39,6 +40,7 @@ describe("scripts/ios-release-signing.mjs", () => {
|
|||
expect(output).toContain("Signing branch: main");
|
||||
expect(output).toContain("Signing setup and sync: Fastlane match");
|
||||
expect(output).not.toContain("OpenClawWatchExtension");
|
||||
expect(output).toContain("capabilities: PUSH_NOTIFICATIONS");
|
||||
expect(output).toContain("capabilities: PUSH_NOTIFICATIONS, APP_GROUPS");
|
||||
expect(output).toContain("app groups: group.ai.openclawfoundation.app.shared");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue