mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-01-23 02:36:05 +00:00
feat(ios): add GitHub Actions workflow for iOS App Store release
- Create build-ios.yml workflow triggered on releases - Configure signing keychain with iOS distribution certificate - Install provisioning profile for App Store distribution - Sync version from package.json using agvtool - Build, archive, and export IPA with manual code signing - Validate and upload to App Store Connect via xcrun altool - Add sync:ios and dist:ios:prod npm scripts
This commit is contained in:
parent
0345c0c11c
commit
2996aaa361
2 changed files with 204 additions and 0 deletions
202
.github/workflows/build-ios.yml
vendored
Normal file
202
.github/workflows/build-ios.yml
vendored
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
name: iOS App Store Release on Release
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
inputs: {}
|
||||
|
||||
jobs:
|
||||
ios-app-store-release:
|
||||
runs-on: macos-latest
|
||||
env:
|
||||
UNSPLASH_KEY: ${{ secrets.UNSPLASH_KEY }}
|
||||
UNSPLASH_CLIENT_ID: ${{ secrets.UNSPLASH_CLIENT_ID }}
|
||||
|
||||
# TODO: Re-enable pre-release skip once workflow is tested
|
||||
# if: '!github.event.release.prerelease'
|
||||
|
||||
steps:
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
# 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:
|
||||
IOS_DISTRIBUTION_CERTIFICATE_BASE64: ${{ secrets.IOS_DISTRIBUTION_CERTIFICATE_BASE64 }}
|
||||
IOS_DISTRIBUTION_CERTIFICATE_PASSWORD: ${{ secrets.IOS_DISTRIBUTION_CERTIFICATE_PASSWORD }}
|
||||
run: |
|
||||
CERT_PATH="$RUNNER_TEMP/ios-dist-cert.p12"
|
||||
KEYCHAIN_NAME="build.keychain"
|
||||
KEYCHAIN_PATH="$HOME/Library/Keychains/${KEYCHAIN_NAME}-db"
|
||||
echo "$IOS_DISTRIBUTION_CERTIFICATE_BASE64" | 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 "$IOS_DISTRIBUTION_CERTIFICATE_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_PROVISIONING_PROFILE_BASE64: ${{ secrets.IOS_PROVISIONING_PROFILE_BASE64 }}
|
||||
run: |
|
||||
PROFILE_PATH="$RUNNER_TEMP/ios-provisioning.mobileprovision"
|
||||
echo "$IOS_PROVISIONING_PROFILE_BASE64" | 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 ==="
|
||||
/usr/libexec/PlistBuddy -c "Print Name" /tmp/profile.plist
|
||||
/usr/libexec/PlistBuddy -c "Print TeamIdentifier:0" /tmp/profile.plist
|
||||
echo "=================================="
|
||||
|
||||
- name: Get npm cache directory
|
||||
id: npm-cache-dir
|
||||
run: |
|
||||
echo "::set-output name=dir::$(npm config get cache)"
|
||||
|
||||
- uses: actions/cache@v5
|
||||
id: npm-cache
|
||||
with:
|
||||
path: ${{ steps.npm-cache-dir.outputs.dir }}
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
|
||||
- name: Install npm Packages
|
||||
run: npm i
|
||||
|
||||
- name: Generate environment
|
||||
run: npm run env
|
||||
|
||||
- name: Set iOS version from package.json
|
||||
run: |
|
||||
VERSION=$(node -p "require('./package.json').version")
|
||||
BUILD_NUMBER=$(date +%Y%m%d%H%M)
|
||||
echo "Setting iOS version to $VERSION (build $BUILD_NUMBER)"
|
||||
|
||||
# Update the project.pbxproj with the correct version
|
||||
cd ios/App
|
||||
# Use agvtool to set versions (it handles all the project file updates)
|
||||
xcrun agvtool new-marketing-version "$VERSION"
|
||||
xcrun agvtool new-version -all "$BUILD_NUMBER"
|
||||
|
||||
- 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
|
||||
xcodebuild archive \
|
||||
-workspace App.xcworkspace \
|
||||
-scheme App \
|
||||
-configuration Release \
|
||||
-archivePath "$RUNNER_TEMP/App.xcarchive" \
|
||||
-destination 'generic/platform=iOS' \
|
||||
CODE_SIGN_STYLE=Manual \
|
||||
PROVISIONING_PROFILE_SPECIFIER="${{ env.PROVISIONING_PROFILE_UUID }}" \
|
||||
CODE_SIGN_IDENTITY="Apple Distribution" \
|
||||
DEVELOPMENT_TEAM="${{ secrets.APPLE_TEAM_ID }}"
|
||||
|
||||
- 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.superproductivity.superproductivity</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@v6
|
||||
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 }}
|
||||
|
|
@ -99,6 +99,8 @@
|
|||
"startFrontend:prod": "npm run env && ng serve --configuration production",
|
||||
"startFrontend:stage": "npm run env && ng serve --configuration stage",
|
||||
"sync:android": "npx cap sync android",
|
||||
"sync:ios": "npx cap sync ios",
|
||||
"dist:ios:prod": "npm run buildFrontend:prodWeb && npm run sync:ios",
|
||||
"stats": "ng build --configuration production --source-map --stats-json && npx esbuild-visualizer --metadata .tmp/angular-dist/stats.json && xdg-open stats.html",
|
||||
"test": "cross-env TZ='Europe/Berlin' ng test --watch=false && npm run test:tz:ci",
|
||||
"test:once": "cross-env TZ='Europe/Berlin' ng test --watch=false",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue