super-productivity/.github/workflows/build-ios.yml
Johannes Millan 01e30b9c7e
Feat/to me it looks like there are lots of 60dd04 (#7280)
* fix(issue): prevent crash from orphan issueProviderId (#7135)

The Jira image-headers effect in task-detail-panel subscribed to
selectIssueProviderById without an error handler, so a task with an
issueProviderId pointing at a deleted provider (e.g. after sync
convergence where taskIdsToUnlink didn't cover all local tasks)
propagated the selector throw to Zone.js as a crash dialog. Wrap the
inner selector observable in catchError that logs and falls back to
of(null); the downstream jiraCfg?.isEnabled guard handles the fallback.

Also drop IssueLog.log(issueProviderKey, issueProvider) from the
throwing variant of the selector: providers may carry credentials
(host, token, apiKey) and IssueLog history is exportable.

* fix(focus-mode): sync tray countdown with in-app timer during breaks

Tray title was rebuilt from a cached currentFocusSessionTime that only
refreshed when CURRENT_TASK_UPDATED fired. addTimeSpent is gated on an
active current task, so during focus-mode breaks or task-less focus
sessions the cache froze while the in-app timer kept ticking.

Add the tick action to taskChangeElectron$ so the cache refreshes every
second whenever the focus timer is running.

Fixes #7278

* fix(ci): restore GitHub Actions SHA pins undone by 0e9218bd68

Commit 0e9218bd68 silently reverted PR #7212 (github-actions-minor group
bump) along with its stated sync/client-id work. This restores the 15
workflow files to their pre-revert state.

Actions restored to newer pinned SHAs:
- actions/upload-artifact v7.0.0 -> v7.0.1
- step-security/harden-runner v2.16.1 -> v2.17.0
- softprops/action-gh-release v2.6.1 -> v3.0.0
- signpath/github-action-submit-signing-request v2.0 -> v2.1
- anthropics/claude-code-action v1.0.89 -> v1.0.93
- docker/build-push-action v7.0.0 -> v7.1.0
- easingthemes/ssh-deploy v5.1.1 -> v6.0.3

* fix: restore i18n, UI, and docs work undone by 0e9218bd68

Commit 0e9218bd68 silently reverted the following work alongside its stated
sync/client-id changes. Files where later master commits (fec7b25f23, etc.)
already re-applied the reverted work are intentionally left untouched.

Restored:
- #7232 docs/long-term-plans/location-based-reminders.md (513 lines)
- #7199 Romanian i18n phase 3 (ro.json + ro-md.json, ~1168 lines)
- #7049 Polish translation improvements
- #7143 planner component styling (4 scss files)
- #7211 add-task-bar preserve time estimate when typing title
- #7208 task.reducer roll-up estimates for added subtasks
- #6767 focus-mode pomodoro reset button
- #7205 plugin-dev github-issue-provider TOKEN description
- #7231 mobile-bottom-nav FAB fix
- a4fe03272 iOS keyboard accessory bar (global-theme + dialog-fullscreen-markdown)
- 309670db3 ShortSyntaxEffects undefined guard
- 667a7986f Dropbox PKCE auth comment/behavior

* fix(electron): restore electron + e2e work undone by 0e9218bd68

Commit 0e9218bd68 silently reverted the following electron/e2e work.
Files already re-fixed by later master commits are left as-is:
- e2e/tests/sync/supersync-archive-conflict.spec.ts (de33234976 + 191d129ff3)

Restored:
- e8a3e156eb fix(electron): Linux autostart IDB backing-store recovery
  (re-adds electron/clear-stale-idb-locks.ts + start-app.ts wiring)
- 5ce78a5b63 fix(electron): macOS Cmd+Q / Dock > Quit hang
  (setIsQuiting + before-quit delegate to close-handler)
- 46e0fa2d01 fix(sync): FILE_SYNC_LIST_FILES IPC contract
  (electronAPI.d.ts + local-file-sync + preload + ipc-events)
- ea1ef16307 fix(android): session-only SAF permissions on OEM devices
- 8865dc0a50 test(e2e): supersync parallel-worker stampede guard
  (SUPERSYNC_SERVER_HEALTHY env-var fallback + goto retry loop)
- af7c7687e2 test(e2e): block WS-triggered downloads in non-WS specs
- 265b44db5d test(e2e): premature waitForURL on daily-summary
  (this is literally the fix the bad commit's message claimed to add)

Conflict resolutions:
- e2e/utils/supersync-helpers.ts: kept the refined getDoneTaskElement
  checks from d64014d086 (later than c558bcab5e) while restoring the
  goto retry loop from 8865dc0a50.
- electron/start-app.ts: unioned imports (setIsQuiting +
  clearStaleLevelDbLocks from theirs, fs from ours).

* fix(sync): restore sync-core work undone by 0e9218bd68

Commit 0e9218bd68 silently reverted parts of several sync fixes. Most
sync-core reverts have already been re-addressed differently on master
by later commits (1f5184f6e7, 05cd875dd6, 09f5ced2c9, 7df43358ab,
d9158d6adb, 32dbc95ed9, 8c3b08e016, f89fe1ebc3) — those files are
intentionally left untouched to avoid reverting master's newer work.

This PR restores only the pieces that are genuinely still missing:

- e8a3e156eb fix(electron): IDB backing-store autoreload (in-app piece)
  operation-log-hydrator.service.ts + .spec.ts (the electron/clear-
  stale-idb-locks.ts piece was restored in the prior commit)

Plus three low-risk documentation/cleanup restorations:
- operation-sync.util.ts — add "Nextcloud" to isFileBasedProvider JSDoc
- dropbox.ts — restore improved _getRedirectUri JSDoc (667a7986fb)
- dialog-get-and-enter-auth-code.component.ts — restore isNativePlatform
  comment explaining why manual code entry flow is used (667a7986fb)
- file-adapter.interface.ts — remove stray "// NEW" comment (46e0fa2d01)

Intentionally NOT restored (master's newer work covers or supersedes):
- sync-trigger.service.ts / sync.effects.ts (05cd875dd6)
- sync-wrapper.service.ts (1f5184f6e7)
- sync-errors.ts (1f5184f6e7 re-added LegacySyncFormatDetectedError)
- file-based-sync-adapter.service.ts + spec (1f5184f6e7 + d9158d6adb)
- file-based-sync.types.ts (1f5184f6e7)
- operation-log.const.ts (32dbc95ed9 bumped IDB_OPEN_RETRIES to 5)
- dialog-sync-initial-cfg.component.ts (f89fe1ebc3)
2026-04-20 12:04:38 +02:00

226 lines
9.2 KiB
YAML

name: iOS App Store Release on Release
on:
push:
tags:
- v*
workflow_dispatch:
inputs: {}
jobs:
ios-app-store-release:
runs-on: macos-15
env:
UNSPLASH_KEY: ${{ secrets.UNSPLASH_KEY }}
UNSPLASH_CLIENT_ID: ${{ secrets.UNSPLASH_CLIENT_ID }}
steps:
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
with:
node-version: 22
- name: Check out Git repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Verify Xcode and SDK version
run: |
echo "=== Xcode Version ==="
xcodebuild -version
echo ""
echo "=== SDK Information ==="
xcrun --show-sdk-version
xcodebuild -showsdks | grep -E "(iOS|iphoneos)"
# work around for npm installs from git+https://github.com/johannesjo/J2M.git
- name: Reconfigure git to use HTTP authentication
run: >
git config --global url."https://github.com/".insteadOf
ssh://git@github.com/
- name: Install Apple WWDR intermediate certificate
run: |
curl -fsSL https://www.apple.com/certificateauthority/AppleWWDRCAG4.cer -o /tmp/AppleWWDRCAG4.cer
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain /tmp/AppleWWDRCAG4.cer
- name: Configure signing keychain
env:
# Reuse mac_certs - "Apple Distribution" certificate works for both macOS and iOS
MAC_CERTS: ${{ secrets.mac_certs }}
MAC_CERTS_PASSWORD: ${{ secrets.mac_certs_password }}
run: |
CERT_PATH="$RUNNER_TEMP/dist-cert.p12"
KEYCHAIN_NAME="build.keychain"
KEYCHAIN_PATH="$HOME/Library/Keychains/${KEYCHAIN_NAME}-db"
echo "$MAC_CERTS" | base64 --decode > "$CERT_PATH"
echo "=== DIAGNOSTIC: Decoded .p12 SHA256 ==="
shasum -a 256 "$CERT_PATH"
echo "========================================"
security create-keychain -p "" "$KEYCHAIN_NAME"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
security unlock-keychain -p "" "$KEYCHAIN_PATH"
curl -fsSL https://www.apple.com/certificateauthority/AppleWWDRCAG4.cer -o "$RUNNER_TEMP/AppleWWDRCAG4.cer"
security import "$RUNNER_TEMP/AppleWWDRCAG4.cer" -k "$KEYCHAIN_PATH" -T /usr/bin/codesign
security import "$CERT_PATH" -k "$KEYCHAIN_PATH" -P "$MAC_CERTS_PASSWORD" -T /usr/bin/codesign -T /usr/bin/security
security list-keychains -s "$KEYCHAIN_PATH" "$HOME/Library/Keychains/login.keychain-db"
security default-keychain -s "$KEYCHAIN_PATH"
security set-key-partition-list -S apple-tool:,apple: -k "" "$KEYCHAIN_PATH"
security find-identity -v -p codesigning "$KEYCHAIN_PATH"
- name: List available signing identities
run: security find-identity -v -p codesigning
- name: Install provisioning profile
env:
IOS_PROVISION_PROFILE: ${{ secrets.IOS_PROVISION_PROFILE }}
run: |
PROFILE_PATH="$RUNNER_TEMP/ios-provisioning.mobileprovision"
echo "$IOS_PROVISION_PROFILE" | base64 --decode > "$PROFILE_PATH"
# Get the UUID from the provisioning profile
PROFILE_UUID=$(/usr/libexec/PlistBuddy -c "Print UUID" /dev/stdin <<< $(security cms -D -i "$PROFILE_PATH"))
echo "Profile UUID: $PROFILE_UUID"
# Create the provisioning profiles directory if it doesn't exist
mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles"
# Copy the profile with its UUID as the filename
cp "$PROFILE_PATH" "$HOME/Library/MobileDevice/Provisioning Profiles/${PROFILE_UUID}.mobileprovision"
echo "PROVISIONING_PROFILE_UUID=$PROFILE_UUID" >> "$GITHUB_ENV"
- name: Verify provisioning profile
run: |
security cms -D -i "$HOME/Library/MobileDevice/Provisioning Profiles/${PROVISIONING_PROFILE_UUID}.mobileprovision" > /tmp/profile.plist
echo "=== PROVISIONING PROFILE INFO ==="
echo "Name: $(/usr/libexec/PlistBuddy -c "Print Name" /tmp/profile.plist)"
echo "Team: $(/usr/libexec/PlistBuddy -c "Print TeamIdentifier:0" /tmp/profile.plist)"
PROFILE_APP_ID=$(/usr/libexec/PlistBuddy -c "Print Entitlements:application-identifier" /tmp/profile.plist | sed 's/^[A-Z0-9]*\.//')
echo "App ID: $PROFILE_APP_ID"
echo "=================================="
# Verify bundle ID matches
EXPECTED_BUNDLE_ID="com.super-productivity.app"
if [ "$PROFILE_APP_ID" != "$EXPECTED_BUNDLE_ID" ]; then
echo "ERROR: Provisioning profile app ID ($PROFILE_APP_ID) does not match expected bundle ID ($EXPECTED_BUNDLE_ID)"
echo "Please create a new provisioning profile with the correct bundle ID"
exit 1
fi
echo "Bundle ID verification passed!"
- name: Get npm cache directory
id: npm-cache-dir
run: |
echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT
- uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
id: npm-cache
with:
path: ${{ steps.npm-cache-dir.outputs.dir }}
key: ${{ runner.os }}-node22-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node22-
- name: Install npm Packages
run: npm i
- name: Generate environment
run: npm run env
- name: Set iOS version from package.json
run: |
FULL_VERSION=$(node -p "require('./package.json').version")
# Strip pre-release suffix (e.g., "17.0.0-RC.3" -> "17.0.0")
# App Store requires X.Y.Z format; build number differentiates uploads
VERSION=$(echo "$FULL_VERSION" | sed 's/-.*//')
BUILD_NUMBER=$(date +%Y%m%d%H%M)
echo "Setting iOS version to $VERSION (build $BUILD_NUMBER) [from $FULL_VERSION]"
cd ios/App
if ! xcrun agvtool new-marketing-version "$VERSION"; then
echo "Error: Failed to set marketing version to $VERSION"
exit 1
fi
if ! xcrun agvtool new-version -all "$BUILD_NUMBER"; then
echo "Error: Failed to set build number to $BUILD_NUMBER"
exit 1
fi
- name: Build Angular frontend
run: npm run buildFrontend:prodWeb
- name: Sync Capacitor iOS
run: npm run sync:ios
- name: Install CocoaPods
run: |
cd ios/App
pod install
- name: Archive iOS app
run: |
cd ios/App
# Archive without code signing - signing is handled during export
# This avoids conflicts with CocoaPods frameworks
xcodebuild archive \
-workspace App.xcworkspace \
-scheme App \
-configuration Release \
-archivePath "$RUNNER_TEMP/App.xcarchive" \
-destination 'generic/platform=iOS' \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO
- name: Create ExportOptions.plist
run: |
cat > "$RUNNER_TEMP/ExportOptions.plist" << EOF
<?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>method</key>
<string>app-store-connect</string>
<key>teamID</key>
<string>${{ secrets.APPLE_TEAM_ID }}</string>
<key>uploadSymbols</key>
<true/>
<key>signingStyle</key>
<string>manual</string>
<key>provisioningProfiles</key>
<dict>
<key>com.super-productivity.app</key>
<string>${{ env.PROVISIONING_PROFILE_UUID }}</string>
</dict>
</dict>
</plist>
EOF
- name: Export IPA
run: |
xcodebuild -exportArchive \
-archivePath "$RUNNER_TEMP/App.xcarchive" \
-exportPath "$RUNNER_TEMP/ipa-output" \
-exportOptionsPlist "$RUNNER_TEMP/ExportOptions.plist"
- name: List exported files
run: ls -la "$RUNNER_TEMP/ipa-output"
- name: Upload IPA artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: sup-ios-release
path: ${{ runner.temp }}/ipa-output/*.ipa
- name: Validate App
run: |
xcrun altool --type ios --validate-app \
-f "$RUNNER_TEMP/ipa-output/"*.ipa \
-u "${{ secrets.APPLE_ID }}" \
-p "${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}"
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
- name: Upload to App Store Connect
run: |
xcrun altool --type ios --upload-app \
-f "$RUNNER_TEMP/ipa-output/"*.ipa \
-u "${{ secrets.APPLE_ID }}" \
-p "${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}"
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}