Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/actionlint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"'
17 changes: 13 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ jobs:
run: >-
xcodebuild test
-workspace Quorra.xcworkspace
-scheme quorra
-scheme QuorraApp
-testPlan quorra
-destination 'platform=macOS'
-clonedSourcePackagesDirPath "$SOURCE_PACKAGES_PATH"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
50 changes: 26 additions & 24 deletions docs/architecture/cli-background-roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand All @@ -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

Expand Down Expand Up @@ -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,
Expand Down
12 changes: 7 additions & 5 deletions quorra.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down