Merge pull request #12 from alexmodrono/worktree-release-0.1 #25
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| permissions: | |
| contents: write | |
| env: | |
| APP_NAME: Findle | |
| SCHEME: Foodle | |
| BUNDLE_ID: es.amodrono.foodle | |
| jobs: | |
| build-and-release: | |
| runs-on: macos-26 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Extract version from tag | |
| id: version | |
| run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" | |
| - name: Install XcodeGen | |
| run: brew install xcodegen | |
| - name: Import signing certificate | |
| env: | |
| CERTIFICATE_P12_BASE64: ${{ secrets.CERTIFICATE_P12_BASE64 }} | |
| CERTIFICATE_PASSWORD: ${{ secrets.CERTIFICATE_PASSWORD }} | |
| run: | | |
| CERT_PATH="$RUNNER_TEMP/certificate.p12" | |
| KEYCHAIN_PATH="$RUNNER_TEMP/build.keychain" | |
| echo -n "$CERTIFICATE_P12_BASE64" | base64 --decode -o "$CERT_PATH" | |
| security create-keychain -p "" "$KEYCHAIN_PATH" | |
| security set-keychain-settings "$KEYCHAIN_PATH" | |
| security unlock-keychain -p "" "$KEYCHAIN_PATH" | |
| security import "$CERT_PATH" -k "$KEYCHAIN_PATH" -P "$CERTIFICATE_PASSWORD" -T /usr/bin/codesign | |
| security set-key-partition-list -S apple-tool:,apple: -k "" "$KEYCHAIN_PATH" | |
| security list-keychains -d user -s "$KEYCHAIN_PATH" login.keychain | |
| - name: Install provisioning profiles | |
| env: | |
| APP_PROVISION_PROFILE_BASE64: ${{ secrets.APP_PROVISION_PROFILE_BASE64 }} | |
| FILEPROVIDER_PROVISION_PROFILE_BASE64: ${{ secrets.FILEPROVIDER_PROVISION_PROFILE_BASE64 }} | |
| run: | | |
| PROFILES_DIR="$HOME/Library/MobileDevice/Provisioning Profiles" | |
| mkdir -p "$PROFILES_DIR" | |
| echo -n "$APP_PROVISION_PROFILE_BASE64" | base64 --decode -o "$PROFILES_DIR/app.provisionprofile" | |
| echo -n "$FILEPROVIDER_PROVISION_PROFILE_BASE64" | base64 --decode -o "$PROFILES_DIR/fileprovider.provisionprofile" | |
| APP_UUID=$(/usr/libexec/PlistBuddy -c "Print UUID" /dev/stdin <<< "$(security cms -D -i "$PROFILES_DIR/app.provisionprofile")") | |
| FP_UUID=$(/usr/libexec/PlistBuddy -c "Print UUID" /dev/stdin <<< "$(security cms -D -i "$PROFILES_DIR/fileprovider.provisionprofile")") | |
| mv "$PROFILES_DIR/app.provisionprofile" "$PROFILES_DIR/$APP_UUID.provisionprofile" | |
| mv "$PROFILES_DIR/fileprovider.provisionprofile" "$PROFILES_DIR/$FP_UUID.provisionprofile" | |
| echo "APP_PROFILE_UUID=$APP_UUID" >> "$GITHUB_ENV" | |
| echo "FP_PROFILE_UUID=$FP_UUID" >> "$GITHUB_ENV" | |
| - name: Write signing xcconfig files | |
| run: | | |
| KEYCHAIN_PATH="$RUNNER_TEMP/build.keychain" | |
| cat > Config/Release-App.xcconfig <<XCCONFIG | |
| CODE_SIGN_STYLE = Manual | |
| CODE_SIGN_IDENTITY = Developer ID Application | |
| DEVELOPMENT_TEAM = ${{ secrets.TEAM_ID }} | |
| PROVISIONING_PROFILE = $APP_PROFILE_UUID | |
| OTHER_CODE_SIGN_FLAGS = --keychain $KEYCHAIN_PATH | |
| XCCONFIG | |
| cat > Config/Release-FileProvider.xcconfig <<XCCONFIG | |
| CODE_SIGN_STYLE = Manual | |
| CODE_SIGN_IDENTITY = Developer ID Application | |
| DEVELOPMENT_TEAM = ${{ secrets.TEAM_ID }} | |
| PROVISIONING_PROFILE = $FP_PROFILE_UUID | |
| OTHER_CODE_SIGN_FLAGS = --keychain $KEYCHAIN_PATH | |
| XCCONFIG | |
| cat > Config/Release-Framework.xcconfig <<XCCONFIG | |
| CODE_SIGN_STYLE = Manual | |
| CODE_SIGN_IDENTITY = - | |
| DEVELOPMENT_TEAM = ${{ secrets.TEAM_ID }} | |
| XCCONFIG | |
| - name: Generate Xcode project | |
| run: xcodegen generate | |
| - name: Set version in Info.plist | |
| run: | | |
| /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString ${{ steps.version.outputs.version }}" Resources/Info/App-Info.plist | |
| /usr/libexec/PlistBuddy -c "Set :CFBundleVersion ${{ github.run_number }}" Resources/Info/App-Info.plist | |
| - name: Build archive | |
| run: | | |
| xcodebuild archive \ | |
| -project Foodle.xcodeproj \ | |
| -scheme "$SCHEME" \ | |
| -configuration Release \ | |
| -archivePath "$RUNNER_TEMP/Foodle.xcarchive" | |
| - name: Export archive | |
| 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>developer-id</string> | |
| <key>teamID</key> | |
| <string>${{ secrets.TEAM_ID }}</string> | |
| <key>signingStyle</key> | |
| <string>manual</string> | |
| <key>signingCertificate</key> | |
| <string>Developer ID Application</string> | |
| <key>provisioningProfiles</key> | |
| <dict> | |
| <key>es.amodrono.foodle</key> | |
| <string>${APP_PROFILE_UUID}</string> | |
| <key>es.amodrono.foodle.file-provider</key> | |
| <string>${FP_PROFILE_UUID}</string> | |
| </dict> | |
| </dict> | |
| </plist> | |
| EOF | |
| xcodebuild -exportArchive \ | |
| -archivePath "$RUNNER_TEMP/Foodle.xcarchive" \ | |
| -exportPath "$RUNNER_TEMP/export" \ | |
| -exportOptionsPlist "$RUNNER_TEMP/ExportOptions.plist" | |
| - name: Notarize app | |
| env: | |
| APPLE_ID: ${{ secrets.APPLE_ID }} | |
| NOTARIZATION_PASSWORD: ${{ secrets.NOTARIZATION_PASSWORD }} | |
| TEAM_ID: ${{ secrets.TEAM_ID }} | |
| run: | | |
| APP_PATH="$RUNNER_TEMP/export/$APP_NAME.app" | |
| ditto -c -k --keepParent "$APP_PATH" "$RUNNER_TEMP/notarize.zip" | |
| xcrun notarytool submit "$RUNNER_TEMP/notarize.zip" \ | |
| --apple-id "$APPLE_ID" \ | |
| --password "$NOTARIZATION_PASSWORD" \ | |
| --team-id "$TEAM_ID" \ | |
| --wait | |
| xcrun stapler staple "$APP_PATH" | |
| - name: Create DMG | |
| run: | | |
| brew install create-dmg | |
| create-dmg \ | |
| --volname "$APP_NAME" \ | |
| --window-pos 200 120 \ | |
| --window-size 600 400 \ | |
| --icon-size 100 \ | |
| --icon "$APP_NAME.app" 150 185 \ | |
| --app-drop-link 450 185 \ | |
| --hide-extension "$APP_NAME.app" \ | |
| "$RUNNER_TEMP/$APP_NAME-${{ steps.version.outputs.version }}.dmg" \ | |
| "$RUNNER_TEMP/export/$APP_NAME.app" | |
| - name: Create ZIP | |
| run: | | |
| cd "$RUNNER_TEMP/export" | |
| ditto -c -k --keepParent "$APP_NAME.app" "$RUNNER_TEMP/$APP_NAME-${{ steps.version.outputs.version }}.zip" | |
| - name: Sign ZIP with Sparkle EdDSA key | |
| id: sparkle | |
| env: | |
| SPARKLE_PRIVATE_ED_KEY: ${{ secrets.SPARKLE_PRIVATE_ED_KEY }} | |
| run: | | |
| ZIP_PATH="$RUNNER_TEMP/$APP_NAME-${{ steps.version.outputs.version }}.zip" | |
| ZIP_SIZE=$(stat -f%z "$ZIP_PATH") | |
| # Find Sparkle's sign_update tool from the SPM build | |
| SIGN_UPDATE=$(find ~/Library/Developer/Xcode/DerivedData -name "sign_update" -path "*/Sparkle-*/bin/*" 2>/dev/null | head -1) | |
| if [ -z "$SIGN_UPDATE" ]; then | |
| # Fallback: build it from the checked-out package | |
| SPARKLE_CHECKOUT=$(find ~/Library/Developer/Xcode/DerivedData -name "Sparkle" -path "*/SourcePackages/checkouts/*" -type d 2>/dev/null | head -1) | |
| if [ -n "$SPARKLE_CHECKOUT" ]; then | |
| pushd "$SPARKLE_CHECKOUT" | |
| xcodebuild -project Sparkle.xcodeproj -scheme sign_update -configuration Release -derivedDataPath "$RUNNER_TEMP/sparkle-build" build 2>/dev/null || true | |
| popd | |
| SIGN_UPDATE=$(find "$RUNNER_TEMP/sparkle-build" -name "sign_update" -type f 2>/dev/null | head -1) | |
| fi | |
| fi | |
| if [ -n "$SIGN_UPDATE" ]; then | |
| ED_SIGNATURE=$("$SIGN_UPDATE" "$ZIP_PATH" --ed-key-file <(echo "$SPARKLE_PRIVATE_ED_KEY") 2>&1 | grep "sparkle:edSignature" | sed 's/.*sparkle:edSignature="\([^"]*\)".*/\1/') | |
| fi | |
| # Fallback: use openssl to compute EdDSA signature manually if sign_update failed | |
| if [ -z "$ED_SIGNATURE" ]; then | |
| echo "Warning: Could not compute EdDSA signature with sign_update, appcast will lack signature" | |
| ED_SIGNATURE="" | |
| fi | |
| echo "ed_signature=$ED_SIGNATURE" >> "$GITHUB_OUTPUT" | |
| echo "zip_size=$ZIP_SIZE" >> "$GITHUB_OUTPUT" | |
| - name: Compute SHA256 | |
| id: sha | |
| run: | | |
| SHA=$(shasum -a 256 "$RUNNER_TEMP/$APP_NAME-${{ steps.version.outputs.version }}.zip" | awk '{print $1}') | |
| echo "sha256=$SHA" >> "$GITHUB_OUTPUT" | |
| - name: Generate Sparkle appcast | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| BUILD="${{ github.run_number }}" | |
| ZIP_SIZE="${{ steps.sparkle.outputs.zip_size }}" | |
| ED_SIGNATURE="${{ steps.sparkle.outputs.ed_signature }}" | |
| REPO="${{ github.repository }}" | |
| PUB_DATE=$(date -u +"%a, %d %b %Y %H:%M:%S %z") | |
| cat > "$RUNNER_TEMP/appcast.xml" <<APPCAST | |
| <?xml version="1.0" encoding="utf-8"?> | |
| <rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" xmlns:dc="http://purl.org/dc/elements/1.1/"> | |
| <channel> | |
| <title>Findle Updates</title> | |
| <language>en</language> | |
| <item> | |
| <title>Version ${VERSION}</title> | |
| <pubDate>${PUB_DATE}</pubDate> | |
| <sparkle:version>${BUILD}</sparkle:version> | |
| <sparkle:shortVersionString>${VERSION}</sparkle:shortVersionString> | |
| <sparkle:minimumSystemVersion>14.0</sparkle:minimumSystemVersion> | |
| <enclosure | |
| url="https://github.com/${REPO}/releases/download/v${VERSION}/Findle-${VERSION}.zip" | |
| length="${ZIP_SIZE}" | |
| type="application/octet-stream" | |
| sparkle:edSignature="${ED_SIGNATURE}" | |
| /> | |
| </item> | |
| </channel> | |
| </rss> | |
| APPCAST | |
| - name: Create GitHub Release and upload artifacts | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| gh release create "$GITHUB_REF_NAME" \ | |
| --title "$APP_NAME ${{ steps.version.outputs.version }}" \ | |
| --generate-notes \ | |
| "$RUNNER_TEMP/$APP_NAME-${{ steps.version.outputs.version }}.dmg" \ | |
| "$RUNNER_TEMP/$APP_NAME-${{ steps.version.outputs.version }}.zip" \ | |
| "$RUNNER_TEMP/appcast.xml" | |
| - name: Update Homebrew cask | |
| env: | |
| HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| SHA="${{ steps.sha.outputs.sha256 }}" | |
| REPO="${{ github.repository }}" | |
| OWNER="${REPO%%/*}" | |
| git clone "https://x-access-token:${HOMEBREW_TAP_TOKEN}@github.com/${OWNER}/homebrew-tap.git" "$RUNNER_TEMP/homebrew-tap" | |
| mkdir -p "$RUNNER_TEMP/homebrew-tap/Casks" | |
| cat > "$RUNNER_TEMP/homebrew-tap/Casks/findle.rb" <<CASK | |
| cask "findle" do | |
| version "$VERSION" | |
| sha256 "$SHA" | |
| url "https://github.com/${REPO}/releases/download/v#{version}/Findle-#{version}.zip" | |
| name "Findle" | |
| desc "Sync Moodle course content to your Mac via Finder" | |
| homepage "https://github.com/${REPO}" | |
| depends_on macos: ">= :sonoma" | |
| app "Findle.app" | |
| zap trash: [ | |
| "~/Library/Group Containers/group.es.amodrono.foodle", | |
| "~/Library/Containers/es.amodrono.foodle", | |
| "~/Library/Containers/es.amodrono.foodle.file-provider", | |
| ] | |
| end | |
| CASK | |
| cd "$RUNNER_TEMP/homebrew-tap" | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add Casks/findle.rb | |
| git commit -m "Update findle to $VERSION" | |
| git push | |
| - name: Clean up keychain | |
| if: always() | |
| run: security delete-keychain "$RUNNER_TEMP/build.keychain" 2>/dev/null || true |