From 5bcc89573fbd092ea4d328e7a72752853dd5dcc6 Mon Sep 17 00:00:00 2001 From: AJ Beck Date: Mon, 7 Sep 2026 08:05:45 +0100 Subject: [PATCH] fix(release): build signed assets with Xcode 27 --- .github/actionlint.yaml | 8 +++ .github/workflows/release.yml | 17 +++++-- .github/workflows/validate.yml | 2 +- README.md | 2 +- docs/architecture/cli-background-roadmap.md | 50 ++++++++++--------- quorra.xcodeproj/project.pbxproj | 12 +++-- .../{quorra.xcscheme => QuorraApp.xcscheme} | 0 7 files changed, 56 insertions(+), 35 deletions(-) rename quorra.xcodeproj/xcshareddata/xcschemes/{quorra.xcscheme => QuorraApp.xcscheme} (100%) diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml index b14cd07..f0889ec 100644 --- a/.github/actionlint.yaml +++ b/.github/actionlint.yaml @@ -3,3 +3,11 @@ self-hosted-runner: labels: - xcode-27 + +# actionlint 1.7.12 ships older metadata for create-github-app-token v3. +# The current v3 action accepts client-id and deprecates app-id. +paths: + .github/workflows/release.yml: + ignore: + - 'missing input "app-id" which is required by action "actions/create-github-app-token@v3"' + - 'input "client-id" is not defined in action "actions/create-github-app-token@v3"' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ef32c26..9c3c61f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,15 +20,25 @@ permissions: jobs: release: - runs-on: macos-26 + runs-on: xcode-27 timeout-minutes: 45 steps: + - name: Verify Xcode 27 toolchain + run: | + xcode_version="$(xcodebuild -version | awk '/^Xcode / { print $2 }')" + xcodebuild -version + + if [[ "${xcode_version%%.*}" != '27' ]]; then + echo "::error::Release packaging requires Xcode 27; found Xcode $xcode_version." + exit 1 + fi + - name: Create release automation token id: release-token uses: actions/create-github-app-token@v3 with: - app-id: ${{ vars.RELEASE_PLEASE_APP_ID }} + client-id: ${{ vars.RELEASE_PLEASE_CLIENT_ID }} private-key: ${{ secrets.RELEASE_PLEASE_APP_PRIVATE_KEY }} permission-contents: write permission-issues: write @@ -170,14 +180,13 @@ jobs: xcodebuild archive \ -workspace Quorra.xcworkspace \ - -scheme quorra \ + -scheme QuorraApp \ -configuration Release \ -destination 'generic/platform=macOS' \ -clonedSourcePackagesDirPath "$RUNNER_TEMP/SourcePackages" \ -skipPackagePluginValidation \ -archivePath "$archive_path" \ QUORRA_PROVISIONING_PROFILE_SPECIFIER="$PROVISIONING_PROFILE_UUID" \ - CODE_SIGN_IDENTITY='Developer ID Application' \ MARKETING_VERSION="$RELEASE_VERSION" \ CURRENT_PROJECT_VERSION="$GITHUB_RUN_NUMBER" diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 318101c..9b0f264 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -87,7 +87,7 @@ jobs: run: >- xcodebuild test -workspace Quorra.xcworkspace - -scheme quorra + -scheme QuorraApp -testPlan quorra -destination 'platform=macOS' -clonedSourcePackagesDirPath "$SOURCE_PACKAGES_PATH" diff --git a/README.md b/README.md index fb498f2..099d54b 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ cd quorra open Quorra.xcworkspace ``` -Run the `quorra` scheme with Command-R. +Run the `QuorraApp` scheme with Command-R. ## What It Does diff --git a/docs/architecture/cli-background-roadmap.md b/docs/architecture/cli-background-roadmap.md index c3845b0..949e191 100644 --- a/docs/architecture/cli-background-roadmap.md +++ b/docs/architecture/cli-background-roadmap.md @@ -62,9 +62,9 @@ and Xcode launchers. The installed command and Argument Parser root remain **Evidence:** Reusable Quorra logic and tests already live in this package. ArgumentParser documents an executable target with a direct product dependency. The resolved graph currently contains ArgumentParser only transitively. Using a -distinct internal product name prevents Xcode from generating a package scheme -that collides with the app's shared `quorra` scheme; Xcode 27 beta had selected -the ambiguous `quorra (QuorraCore)` scheme before this split. +distinct internal product name keeps the installed command independent of its +build product. The app's shared scheme is explicitly named `QuorraApp`, so it +cannot collide with SwiftPM-generated command schemes in the workspace. ### D002 — Profile loading boundary @@ -289,12 +289,13 @@ extra. **Decision:** Run the complete pull-request test plan on both stable `macos-26` and the `xcode-27` public-preview runner. Preserve the existing stable check name for branch protection and give failed result bundles unique -matrix artifact names. Continue signed release packaging on stable Xcode until -Xcode 27 is generally available. +matrix artifact names. Build signed releases with Xcode 27 because the native +CLI target and its signing configuration are maintained with that toolchain. **Evidence:** GitHub lists `xcode-27` as the Apple-silicon public-preview label. -The release workflow's app-token action also now receives its required -`RELEASE_PLEASE_APP_ID` rather than the unsupported client-ID input. +Version 3 of the release workflow's app-token action deprecates its numeric +app-ID input, so the action receives the repository's +`RELEASE_PLEASE_CLIENT_ID` value. ### D017 — Retain the AWS SDK OIDC client @@ -312,19 +313,20 @@ compiling the AWS graph. ### D018 — Developer ID signing for the embedded CLI -**Decision:** Pass `CODE_SIGN_IDENTITY=Developer ID Application` to the release -archive so every signable target, including `QuorraCLI`, uses the distribution -identity. Keep the main app's manual provisioning profile and verify the -exported helper's authority, hardened runtime, identifier, App Group entitlement, -and nested signature in CI. - -**Evidence:** Xcode 27 evaluates the helper's target-level Release identity as -Apple Development unless overridden. Its only entitlement is the macOS team- -prefix App Group `9GEBAJV9R4.quorra`; Apple documents this form as unrestricted -and not requiring a provisioning profile. The main app still requires its -profile for the restricted Keychain access group. Xcode evaluation confirms the -workflow override reaches the helper without changing its automatic signing -style or adding a profile requirement. +**Decision:** Give `QuorraCLI` explicit per-configuration signing settings: +automatic Apple Development signing for Debug and manual Developer ID signing +for Release. Keep the main app's manual provisioning profile, avoid a global +archive identity override, and verify the exported helper's authority, hardened +runtime, identifier, App Group entitlement, and nested signature in CI. + +**Evidence:** A workspace-wide `CODE_SIGN_IDENTITY` override also reached Swift +package resource bundles and conflicted with the helper's automatic signing. +Its only entitlement is the macOS team-prefix App Group +`9GEBAJV9R4.quorra`; Apple documents this form as unrestricted and not requiring +a provisioning profile. `REGISTER_APP_GROUPS=NO` reflects that unprovisioned +form. The main app still requires its profile for the restricted Keychain access +group. Xcode 27 evaluates both application targets as Developer ID/manual for +Release without applying that identity to package targets. ## Open Decisions @@ -410,10 +412,10 @@ style or adding a profile requirement. - `actionlint` currently flags the inherited release-token configuration: `actions/create-github-app-token@v3` requires `app-id`. The workflow now uses the repository's existing `RELEASE_PLEASE_APP_ID`, and `actionlint` passes. -- The release archive now explicitly applies the Developer ID Application - identity to nested signable targets. Xcode 27 build-setting evaluation proves - the override reaches `QuorraCLI`, and CI rejects an exported helper whose - signing authority is not Developer ID Application. +- The release archive now uses Xcode 27 and the unambiguous `QuorraApp` scheme. + The nested helper owns its Developer ID Release identity, while package targets + receive no workspace-wide signing override. CI rejects an exported helper + whose signing authority is not Developer ID Application. - App-owned profile sign-in is implemented with start/status/cancel IPC operations. Six focused Xcode 27 tests pass, covering state transitions, failure prose, cancellation forwarding, invalid profiles, command parsing, diff --git a/quorra.xcodeproj/project.pbxproj b/quorra.xcodeproj/project.pbxproj index cf6a606..7027c3d 100644 --- a/quorra.xcodeproj/project.pbxproj +++ b/quorra.xcodeproj/project.pbxproj @@ -381,7 +381,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = dev.ajbeck.quorra; PRODUCT_NAME = "$(TARGET_NAME)"; - REGISTER_APP_GROUPS = YES; + REGISTER_APP_GROUPS = NO; STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; @@ -416,7 +416,7 @@ PRODUCT_BUNDLE_IDENTIFIER = dev.ajbeck.quorra; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "$(QUORRA_PROVISIONING_PROFILE_SPECIFIER)"; - REGISTER_APP_GROUPS = YES; + REGISTER_APP_GROUPS = NO; STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; @@ -432,6 +432,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = QuorraCLI/QuorraCLI.entitlements; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; ENABLE_APP_SANDBOX = NO; @@ -459,7 +460,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = dev.ajbeck.quorra.cli; PRODUCT_NAME = "$(TARGET_NAME)"; - REGISTER_APP_GROUPS = YES; + REGISTER_APP_GROUPS = NO; SDKROOT = macosx; SKIP_INSTALL = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES; @@ -481,7 +482,8 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = QuorraCLI/QuorraCLI.entitlements; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; ENABLE_APP_SANDBOX = NO; ENABLE_DEBUG_DYLIB = NO; @@ -508,7 +510,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = dev.ajbeck.quorra.cli; PRODUCT_NAME = "$(TARGET_NAME)"; - REGISTER_APP_GROUPS = YES; + REGISTER_APP_GROUPS = NO; SDKROOT = macosx; SKIP_INSTALL = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES; diff --git a/quorra.xcodeproj/xcshareddata/xcschemes/quorra.xcscheme b/quorra.xcodeproj/xcshareddata/xcschemes/QuorraApp.xcscheme similarity index 100% rename from quorra.xcodeproj/xcshareddata/xcschemes/quorra.xcscheme rename to quorra.xcodeproj/xcshareddata/xcschemes/QuorraApp.xcscheme