diff --git a/.github/workflows/finalize-release.yml b/.github/workflows/finalize-release.yml index 0f90160..21ff57a 100644 --- a/.github/workflows/finalize-release.yml +++ b/.github/workflows/finalize-release.yml @@ -1,19 +1,12 @@ -name: Finalize Release +name: Finalize Notarization (manual) on: - workflow_run: - workflows: - - Release - types: - - completed workflow_dispatch: inputs: release_run_id: description: Release workflow run ID; leave blank to use the latest pending run required: false type: string - schedule: - - cron: "17,47 * * * *" permissions: actions: read diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 110bb6d..0a0efa6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,7 +9,7 @@ on: type: string permissions: - contents: read + contents: write concurrency: group: meetingnotes-release @@ -20,9 +20,7 @@ jobs: runs-on: macos-15 env: VERSION: ${{ inputs.version }} - APPLE_ID: ${{ secrets.APPLE_ID }} - APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} - APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }} + SPARKLE_PRIVATE_KEY: ${{ secrets.SPARKLE_PRIVATE_KEY }} steps: - uses: actions/checkout@v7 with: @@ -33,7 +31,7 @@ jobs: APPLE_CERTIFICATE_P12: ${{ secrets.APPLE_CERTIFICATE_P12 }} APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} run: | - for variable in APPLE_CERTIFICATE_P12 APPLE_CERTIFICATE_PASSWORD APPLE_ID APPLE_TEAM_ID APPLE_APP_PASSWORD; do + for variable in APPLE_CERTIFICATE_P12 APPLE_CERTIFICATE_PASSWORD SPARKLE_PRIVATE_KEY; do if [[ -z "${!variable:-}" ]]; then echo "Missing GitHub Actions secret: $variable" >&2 exit 1 @@ -55,20 +53,19 @@ jobs: fi echo "SIGNING_IDENTITY=$signing_identity" >> "$GITHUB_ENV" - - name: Build, sign, and submit for notarization + - name: Build and sign release timeout-minutes: 30 run: scripts/package_release.sh - - name: Preserve signed build while Apple processes it + - name: Preserve signed release artifacts uses: actions/upload-artifact@v4 with: - name: meetingnotes-notarization-${{ github.run_id }} - path: ${{ runner.temp }}/meetingnotes-release/pending + name: meetingnotes-signed-release-${{ github.run_id }} + path: ${{ runner.temp }}/meetingnotes-release/release if-no-files-found: error retention-days: 30 - - name: Legacy inline publisher (disabled) - if: ${{ false }} + - name: Publish signed GitHub release env: GH_TOKEN: ${{ github.token }} run: | diff --git a/Meetingnotes.xcodeproj/project.pbxproj b/Meetingnotes.xcodeproj/project.pbxproj index 0286b81..a3b2394 100644 --- a/Meetingnotes.xcodeproj/project.pbxproj +++ b/Meetingnotes.xcodeproj/project.pbxproj @@ -278,7 +278,7 @@ COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 15; DEVELOPMENT_ASSET_PATHS = "\"meetingnotes/Preview Content\""; - DEVELOPMENT_TEAM = ML6HYR5LUR; + DEVELOPMENT_TEAM = G9LVHZAJNX; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -293,7 +293,7 @@ MARKETING_VERSION = 1.1.3; ONLY_ACTIVE_ARCH = NO; OTHER_SWIFT_FLAGS = "$(inherited) -D ENABLE_TCC_SPI"; - PRODUCT_BUNDLE_IDENTIFIER = owen.meetingnotes; + PRODUCT_BUNDLE_IDENTIFIER = net.jamesbone.meetingnotes; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = YES; @@ -314,7 +314,7 @@ COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 15; DEVELOPMENT_ASSET_PATHS = "\"meetingnotes/Preview Content\""; - DEVELOPMENT_TEAM = ML6HYR5LUR; + DEVELOPMENT_TEAM = G9LVHZAJNX; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -329,7 +329,7 @@ MARKETING_VERSION = 1.1.3; ONLY_ACTIVE_ARCH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D ENABLE_TCC_SPI"; - PRODUCT_BUNDLE_IDENTIFIER = owen.meetingnotes; + PRODUCT_BUNDLE_IDENTIFIER = net.jamesbone.meetingnotes; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = YES; diff --git a/meetingnotes/Managers/KeychainHelper.swift b/meetingnotes/Managers/KeychainHelper.swift index 3ba052e..30db02b 100644 --- a/meetingnotes/Managers/KeychainHelper.swift +++ b/meetingnotes/Managers/KeychainHelper.swift @@ -8,7 +8,7 @@ import Security class KeychainHelper { static let shared = KeychainHelper() - private let serviceName = "owen.meetingnotes" + private let serviceName = "net.jamesbone.meetingnotes" private init() {} diff --git a/meetingnotes/ProcessTap/AudioProcessController.swift b/meetingnotes/ProcessTap/AudioProcessController.swift index d510033..93e90c2 100644 --- a/meetingnotes/ProcessTap/AudioProcessController.swift +++ b/meetingnotes/ProcessTap/AudioProcessController.swift @@ -49,7 +49,7 @@ extension String: @retroactive LocalizedError { @Observable final class AudioProcessController { - private let logger = Logger(subsystem: "owen.meetingnotes", category: String(describing: AudioProcessController.self)) + private let logger = Logger(subsystem: "net.jamesbone.meetingnotes", category: String(describing: AudioProcessController.self)) private(set) var processes = [AudioProcess]() { didSet { @@ -236,4 +236,4 @@ private extension URL { var isApp: Bool { (try? resourceValues(forKeys: [.contentTypeKey]))?.contentType?.conforms(to: .application) == true } -} \ No newline at end of file +} diff --git a/meetingnotes/ProcessTap/AudioRecordingPermission.swift b/meetingnotes/ProcessTap/AudioRecordingPermission.swift index ad84059..7f6d1c5 100644 --- a/meetingnotes/ProcessTap/AudioRecordingPermission.swift +++ b/meetingnotes/ProcessTap/AudioRecordingPermission.swift @@ -5,7 +5,7 @@ import OSLog /// Uses TCC SPI in order to check/request system audio recording permission. @Observable final class AudioRecordingPermission { - private let logger = Logger(subsystem: "owen.meetingnotes", category: String(describing: AudioRecordingPermission.self)) + private let logger = Logger(subsystem: "net.jamesbone.meetingnotes", category: String(describing: AudioRecordingPermission.self)) enum Status: String { case unknown @@ -122,4 +122,4 @@ final class AudioRecordingPermission { return fn }() #endif // ENABLE_TCC_SPI -} \ No newline at end of file +} diff --git a/meetingnotes/ProcessTap/ProcessTap.swift b/meetingnotes/ProcessTap/ProcessTap.swift index 789ccc3..66e0c3f 100644 --- a/meetingnotes/ProcessTap/ProcessTap.swift +++ b/meetingnotes/ProcessTap/ProcessTap.swift @@ -51,7 +51,7 @@ final class ProcessTap { init(target: TapTarget, muteWhenRunning: Bool = false) { self.target = target self.muteWhenRunning = muteWhenRunning - self.logger = Logger(subsystem: "owen.meetingnotes", category: "\(String(describing: ProcessTap.self))(\(target.loggingProcessName))") + self.logger = Logger(subsystem: "net.jamesbone.meetingnotes", category: "\(String(describing: ProcessTap.self))(\(target.loggingProcessName))") } @ObservationIgnored @@ -327,7 +327,7 @@ final class ProcessTapRecorder { self.tapDisplayName = tap.displayName self.fileURL = fileURL self._tap = tap - self.logger = Logger(subsystem: "owen.meetingnotes", category: "\(String(describing: ProcessTapRecorder.self))(\(fileURL.lastPathComponent))") + self.logger = Logger(subsystem: "net.jamesbone.meetingnotes", category: "\(String(describing: ProcessTapRecorder.self))(\(fileURL.lastPathComponent))") self.icon = tap.target.iconImage } diff --git a/meetingnotes/meetingnotes.entitlements b/meetingnotes/meetingnotes.entitlements index e1c5df7..02c8a9d 100644 --- a/meetingnotes/meetingnotes.entitlements +++ b/meetingnotes/meetingnotes.entitlements @@ -16,8 +16,8 @@ com.apple.security.temporary-exception.mach-lookup.global-name - owen.meetingnotes-spks - owen.meetingnotes-spki + net.jamesbone.meetingnotes-spks + net.jamesbone.meetingnotes-spki diff --git a/scripts/build_release.sh b/scripts/build_release.sh index 11371c0..0c94267 100755 --- a/scripts/build_release.sh +++ b/scripts/build_release.sh @@ -7,7 +7,7 @@ set -e # Exit on any error # Configuration APP_NAME="Meetingnotes" -BUNDLE_ID="owen.meetingnotes" +BUNDLE_ID="net.jamesbone.meetingnotes" VERSION=$(grep -m1 "MARKETING_VERSION" Meetingnotes.xcodeproj/project.pbxproj | sed 's/.*= \(.*\);/\1/') # Source environment variables if .env file exists @@ -379,4 +379,4 @@ echo " 1. Test the DMG on another Mac" echo " 2. Create a GitHub release with tag v${VERSION}" echo " 3. Upload the DMG to the GitHub release" echo " 4. Commit and push the appcast.xml file" -echo " 5. Your users will get auto-update notifications!" \ No newline at end of file +echo " 5. Your users will get auto-update notifications!" diff --git a/scripts/package_release.sh b/scripts/package_release.sh index ff8ab77..55f408d 100755 --- a/scripts/package_release.sh +++ b/scripts/package_release.sh @@ -8,16 +8,14 @@ SCHEME="meetingnotes" RUNNER_TEMP="${RUNNER_TEMP:-/tmp}" BUILD_ROOT="${BUILD_ROOT:-$RUNNER_TEMP/meetingnotes-release}" DERIVED_DATA="$BUILD_ROOT/DerivedData" -PENDING_DIR="$BUILD_ROOT/pending" +RELEASE_DIR="$BUILD_ROOT/release" APP_PATH="$DERIVED_DATA/Build/Products/Release/$APP_NAME.app" required_variables=( VERSION SIGNING_IDENTITY - APPLE_ID - APPLE_TEAM_ID - APPLE_APP_PASSWORD - GITHUB_SHA + SPARKLE_PRIVATE_KEY + GITHUB_REPOSITORY ) for variable in "${required_variables[@]}"; do @@ -34,7 +32,7 @@ if [[ "$project_version" != "$VERSION" ]]; then fi rm -rf "$BUILD_ROOT" -mkdir -p "$PENDING_DIR" +mkdir -p "$RELEASE_DIR" xcodebuild \ -project "$PROJECT" \ @@ -78,31 +76,29 @@ codesign --force --timestamp --options runtime \ codesign --verify --deep --strict --verbose=2 "$APP_PATH" codesign -d --entitlements :- "$APP_PATH" 2>&1 | grep -q 'com.apple.security.app-sandbox' -PRE_NOTARY_ZIP="$PENDING_DIR/$APP_NAME-pre-notary.zip" -ditto -c -k --sequesterRsrc --keepParent "$APP_PATH" "$PRE_NOTARY_ZIP" - -xcrun notarytool submit "$PRE_NOTARY_ZIP" \ - --apple-id "$APPLE_ID" \ - --team-id "$APPLE_TEAM_ID" \ - --password "$APPLE_APP_PASSWORD" \ - --output-format json > "$PENDING_DIR/notary-submission.json" - -SUBMISSION_ID=$(plutil -extract id raw -o - "$PENDING_DIR/notary-submission.json") -echo "Notarization submitted: $SUBMISSION_ID" +ARCHIVE_NAME="$APP_NAME-$VERSION.zip" +ARCHIVE_PATH="$RELEASE_DIR/$ARCHIVE_NAME" +ditto -c -k --sequesterRsrc --keepParent "$APP_PATH" "$ARCHIVE_PATH" GENERATE_APPCAST=$(find "$DERIVED_DATA/SourcePackages/artifacts" -type f -name generate_appcast -print -quit) if [[ -z "$GENERATE_APPCAST" ]]; then echo "Sparkle generate_appcast tool was not found" >&2 exit 1 fi -cp "$GENERATE_APPCAST" "$PENDING_DIR/generate_appcast" -printf '%s' "$VERSION" > "$PENDING_DIR/version" -printf '%s' "$GITHUB_SHA" > "$PENDING_DIR/commit-sha" +DOWNLOAD_URL="https://github.com/$GITHUB_REPOSITORY/releases/download/v$VERSION/" +printf '%s' "$SPARKLE_PRIVATE_KEY" | "$GENERATE_APPCAST" "$RELEASE_DIR" \ + --ed-key-file - \ + --download-url-prefix "$DOWNLOAD_URL" \ + --maximum-deltas 0 \ + -o "$RELEASE_DIR/appcast.xml" + +grep -q "$DOWNLOAD_URL$ARCHIVE_NAME" "$RELEASE_DIR/appcast.xml" +grep -q 'sparkle:edSignature=' "$RELEASE_DIR/appcast.xml" if [[ -n "${GITHUB_STEP_SUMMARY:-}" ]]; then - printf 'Submitted Meetingnotes %s for Apple notarization.\n\nSubmission: `%s`\n\nThe finalize workflow will publish the release after Apple accepts it.\n' \ - "$VERSION" "$SUBMISSION_ID" >> "$GITHUB_STEP_SUMMARY" + printf 'Built and Developer ID-signed Meetingnotes %s. The GitHub release is ready to publish.\n' \ + "$VERSION" >> "$GITHUB_STEP_SUMMARY" fi -echo "Signed app and notarization metadata are ready in $PENDING_DIR" +echo "Signed release artifacts are ready in $RELEASE_DIR"