Skip to content
Open
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
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ jobs:
-derivedDataPath apps/macos/.derivedData-ci \
ARCHS=arm64 \
ONLY_ACTIVE_ARCH=YES \
CODE_SIGNING_ALLOWED=NO \
build

ios-app:
Expand Down
63 changes: 62 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,33 @@ jobs:
if: ${{ env.RELEASE_DRY_RUN != 'true' }}
uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.1

- name: Import Apple certificate
if: ${{ env.RELEASE_DRY_RUN != 'true' }}
env:
APPLE_CERTIFICATE_BASE64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
run: |
CERTIFICATE_PATH="$RUNNER_TEMP/certificate.p12"
KEYCHAIN_PATH="$RUNNER_TEMP/app-signing.keychain-db"
KEYCHAIN_PASSWORD="$(openssl rand -hex 16)"

echo "$APPLE_CERTIFICATE_BASE64" | base64 --decode > "$CERTIFICATE_PATH"

security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"

security import "$CERTIFICATE_PATH" \
-P "$APPLE_CERTIFICATE_PASSWORD" \
-A -t cert -f pkcs12 \
-k "$KEYCHAIN_PATH"

security set-key-partition-list \
-S apple-tool:,apple: \
-k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"

security list-keychain -d user -s "$KEYCHAIN_PATH"

- name: Determine package version
id: version
run: |
Expand All @@ -864,7 +891,6 @@ jobs:
VERSION="0.0.0-dev"
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"

- name: Build Swift bridge artifacts and generate Xcode project
env:
MOLTIS_VERSION: ${{ steps.version.outputs.version }}
Expand All @@ -875,13 +901,18 @@ jobs:
- name: Build macOS app
env:
DERIVED_DATA_DIR: apps/macos/.derivedData-ci
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: |
xcodebuild \
-project apps/macos/Moltis.xcodeproj \
-scheme Moltis \
-configuration Release \
-destination "platform=macOS" \
-derivedDataPath "$DERIVED_DATA_DIR" \
CODE_SIGN_STYLE=Manual \
CODE_SIGN_IDENTITY="Developer ID Application" \
DEVELOPMENT_TEAM="$APPLE_TEAM_ID" \
Comment on lines +912 to +914
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Skip signing in dry-run macOS release builds

In .github/workflows/release.yml, this step now always forces manual Developer ID signing, but the certificate import is gated behind if: ${{ env.RELEASE_DRY_RUN != 'true' }}. For workflow_dispatch dry runs, no signing cert is installed while xcodebuild still requires one, so the macOS job fails before packaging/notarization logic can be exercised. This makes the workflow’s dry-run mode effectively unusable.

Useful? React with 👍 / 👎.

OTHER_CODE_SIGN_FLAGS="--options runtime" \
build
Comment on lines 901 to 916
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Signing parameters applied unconditionally while certificate import is conditional

The "Import Apple certificate" step is gated on RELEASE_DRY_RUN != 'true', but the "Build macOS app" step always runs — including the CODE_SIGN_STYLE=Manual, CODE_SIGN_IDENTITY="Developer ID Application", and DEVELOPMENT_TEAM overrides. During a dry run, no certificate is imported into the keychain, so xcodebuild will fail immediately with "code signing identity 'Developer ID Application' does not match any code signing certificate in your keychain".

You should guard the signing parameters with the same condition, or add a separate code path that builds with CODE_SIGNING_ALLOWED=NO for dry runs. For example:

      - name: Build macOS app (dry run – no signing)
        if: ${{ env.RELEASE_DRY_RUN == 'true' }}
        env:
          DERIVED_DATA_DIR: apps/macos/.derivedData-ci
        run: |
          xcodebuild \
            -project apps/macos/Moltis.xcodeproj \
            -scheme Moltis \
            -configuration Release \
            -destination "platform=macOS" \
            -derivedDataPath "$DERIVED_DATA_DIR" \
            CODE_SIGNING_ALLOWED=NO \
            build

      - name: Build macOS app (release – signed)
        if: ${{ env.RELEASE_DRY_RUN != 'true' }}
        env:
          DERIVED_DATA_DIR: apps/macos/.derivedData-ci
          APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
        run: |
          xcodebuild \
            -project apps/macos/Moltis.xcodeproj \
            -scheme Moltis \
            -configuration Release \
            -destination "platform=macOS" \
            -derivedDataPath "$DERIVED_DATA_DIR" \
            CODE_SIGN_STYLE=Manual \
            CODE_SIGN_IDENTITY="Developer ID Application" \
            DEVELOPMENT_TEAM="$APPLE_TEAM_ID" \
            OTHER_CODE_SIGN_FLAGS="--options runtime" \
            build


- name: Package macOS app
Expand All @@ -895,6 +926,28 @@ jobs:
fi
ditto -c -k --sequesterRsrc --keepParent "$APP_PATH" "moltis-${VERSION}-macos.app.zip"

- name: Notarize macOS app
if: ${{ env.RELEASE_DRY_RUN != 'true' }}
env:
VERSION: ${{ steps.version.outputs.version }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: |
xcrun notarytool submit "moltis-${VERSION}-macos.app.zip" \
--apple-id "$APPLE_ID" \
--password "$APPLE_ID_PASSWORD" \
--team-id "$APPLE_TEAM_ID" \
--wait

# Staple the notarization ticket to the app bundle
APP_PATH="apps/macos/.derivedData-ci/Build/Products/Release/Moltis.app"
xcrun stapler staple "$APP_PATH"

# Re-package with the stapled ticket
rm "moltis-${VERSION}-macos.app.zip"
ditto -c -k --sequesterRsrc --keepParent "$APP_PATH" "moltis-${VERSION}-macos.app.zip"

- name: Sign with Sigstore and generate checksums
if: ${{ env.RELEASE_DRY_RUN != 'true' }}
uses: ./.github/actions/sign-artifacts
Expand All @@ -913,6 +966,14 @@ jobs:
*.app.zip.sig
*.app.zip.crt

- name: Clean up keychain
if: always()
run: |
KEYCHAIN_PATH="$RUNNER_TEMP/app-signing.keychain-db"
if [ -f "$KEYCHAIN_PATH" ]; then
security delete-keychain "$KEYCHAIN_PATH"
fi

build-windows-exe:
needs: [clippy, test, e2e]
runs-on: windows-latest
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ Exact commands (must match `local-validate.sh`):
- Fmt: `cargo +nightly-2025-11-30 fmt --all -- --check`
- Clippy: `cargo +nightly-2025-11-30 clippy -Z unstable-options --workspace --all-features --all-targets --timings -- -D warnings`
- macOS without `nvcc`: clippy without `--all-features`
- macOS app (Darwin hosts): `./scripts/build-swift-bridge.sh && ./scripts/generate-swift-project.sh && ./scripts/lint-swift.sh && xcodebuild -project apps/macos/Moltis.xcodeproj -scheme Moltis -configuration Release -destination "platform=macOS" -derivedDataPath apps/macos/.derivedData-local-validate build`
- macOS app (Darwin hosts): `./scripts/build-swift-bridge.sh && ./scripts/generate-swift-project.sh && ./scripts/lint-swift.sh && xcodebuild -project apps/macos/Moltis.xcodeproj -scheme Moltis -configuration Release -destination "platform=macOS" -derivedDataPath apps/macos/.derivedData-local-validate CODE_SIGNING_ALLOWED=NO build`
- iOS app (Darwin hosts): `cargo run -p moltis-schema-export -- apps/ios/GraphQL/Schema/schema.graphqls && ./scripts/generate-ios-graphql.sh && ./scripts/generate-ios-project.sh && xcodebuild -project apps/ios/Moltis.xcodeproj -scheme Moltis -configuration Debug -destination "generic/platform=iOS" CODE_SIGNING_ALLOWED=NO build`

### PR Descriptions
Expand Down
5 changes: 5 additions & 0 deletions apps/macos/Moltis.entitlements
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?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/>
</plist>
5 changes: 5 additions & 0 deletions apps/macos/project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,18 @@ targets:
sources:
- path: Sources
- path: Assets.xcassets
entitlements:
path: Moltis.entitlements
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: org.moltis.app
GENERATE_INFOPLIST_FILE: YES
SWIFT_EMIT_LOC_STRINGS: NO
ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon
SWIFT_OBJC_BRIDGING_HEADER: Sources/Bridging-Header.h
ENABLE_HARDENED_RUNTIME: YES
CODE_SIGN_STYLE: Manual
CODE_SIGN_IDENTITY: "Developer ID Application"
Comment on lines +40 to +42
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Manual signing identity in base settings breaks Debug builds for most developers

CODE_SIGN_STYLE: Manual and CODE_SIGN_IDENTITY: "Developer ID Application" are placed in the base settings block, which means they apply to both Debug and Release configurations. When a developer opens the project in Xcode and runs a normal Debug build without the CODE_SIGNING_ALLOWED=NO command-line override, Xcode will look for a "Developer ID Application" certificate in their keychain. Most contributors won't have this certificate (it requires a paid account with the specific distribution cert), causing the build to fail immediately in Xcode.

Consider scoping these signing settings to the Release configuration only:

    settings:
      base:
        PRODUCT_BUNDLE_IDENTIFIER: org.moltis.app
        GENERATE_INFOPLIST_FILE: YES
        SWIFT_EMIT_LOC_STRINGS: NO
        ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon
        SWIFT_OBJC_BRIDGING_HEADER: Sources/Bridging-Header.h
        ENABLE_HARDENED_RUNTIME: YES
        OTHER_LDFLAGS:
          ...
      configs:
        Release:
          CODE_SIGN_STYLE: Manual
          CODE_SIGN_IDENTITY: "Developer ID Application"

Comment on lines +41 to +42
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restrict manual Developer ID signing to release builds

Adding manual signing at the target base settings level applies it to all generated macOS builds, not just release packaging. Local developer workflows (just swift-build / just swift-test, via scripts/build-swift.sh and scripts/test-swift.sh) call xcodebuild without CODE_SIGNING_ALLOWED=NO, so machines without a Developer ID certificate will now fail to build/test the app. This is a regression in day-to-day macOS development.

Useful? React with 👍 / 👎.

OTHER_LDFLAGS:
- "-lmoltis_bridge"
- "-lsqlite3"
Expand Down
2 changes: 1 addition & 1 deletion scripts/local-validate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ lint_cmd="${LOCAL_VALIDATE_LINT_CMD:-cargo +${nightly_toolchain} clippy -Z unsta
test_cmd="${LOCAL_VALIDATE_TEST_CMD:-cargo +${nightly_toolchain} nextest run --all-features --profile ci}"
e2e_cmd="${LOCAL_VALIDATE_E2E_CMD:-cd crates/web/ui && if [ ! -d node_modules ]; then npm ci; fi && npm run e2e:install && npm run e2e}"
coverage_cmd="${LOCAL_VALIDATE_COVERAGE_CMD:-cargo +${nightly_toolchain} llvm-cov --workspace --all-features --html}"
macos_app_cmd="${LOCAL_VALIDATE_MACOS_APP_CMD:-./scripts/build-swift-bridge.sh && ./scripts/generate-swift-project.sh && ./scripts/lint-swift.sh && xcodebuild -project apps/macos/Moltis.xcodeproj -scheme Moltis -configuration Release -destination \"platform=macOS\" -derivedDataPath apps/macos/.derivedData-local-validate build}"
macos_app_cmd="${LOCAL_VALIDATE_MACOS_APP_CMD:-./scripts/build-swift-bridge.sh && ./scripts/generate-swift-project.sh && ./scripts/lint-swift.sh && xcodebuild -project apps/macos/Moltis.xcodeproj -scheme Moltis -configuration Release -destination \"platform=macOS\" -derivedDataPath apps/macos/.derivedData-local-validate CODE_SIGNING_ALLOWED=NO build}"
ios_app_cmd="${LOCAL_VALIDATE_IOS_APP_CMD:-cargo run -p moltis-schema-export -- apps/ios/GraphQL/Schema/schema.graphqls && ./scripts/generate-ios-graphql.sh && ./scripts/generate-ios-project.sh && xcodebuild -project apps/ios/Moltis.xcodeproj -scheme Moltis -configuration Debug -destination \"generic/platform=iOS\" CODE_SIGNING_ALLOWED=NO build}"
build_cmd="${LOCAL_VALIDATE_BUILD_CMD:-cargo +${nightly_toolchain} build --workspace --all-features --all-targets}"

Expand Down
Loading