Session pins, presentable Media, Schedule sessions + runner, .tpschedule #35
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: Build & Release | |
| on: | |
| push: | |
| branches: [main] | |
| tags: ['v*'] | |
| pull_request: | |
| branches: [main] | |
| # Cancel in-progress runs for the same branch | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| SCHEME: TopPresenter | |
| PRODUCT_NAME: TopPresenter | |
| XCODE_VERSION: '16.0' | |
| jobs: | |
| build: | |
| name: Build & Package | |
| runs-on: macos-15 | |
| timeout-minutes: 30 | |
| outputs: | |
| version: ${{ steps.package.outputs.VERSION }} | |
| prerelease_tag: ${{ steps.package.outputs.PRERELEASE_TAG }} | |
| prerelease_version: ${{ steps.package.outputs.PRERELEASE_VERSION }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v5 | |
| - name: Select Xcode | |
| run: sudo xcode-select -s /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Developer | |
| - name: Show Xcode version | |
| run: xcodebuild -version | |
| - name: Resolve dependencies | |
| run: xcodebuild -resolvePackageDependencies -scheme "$SCHEME" -project "$PRODUCT_NAME.xcodeproj" | |
| - name: Build | |
| run: | | |
| # Monotonic CFBundleVersion so Sparkle can tell releases apart (the project's | |
| # CURRENT_PROJECT_VERSION is a fixed 1). Ad-hoc code signing ("-") so the app's | |
| # entitlements (sandbox + Sparkle XPC) actually apply and the in-app updater's | |
| # installer works — no Apple certs needed (Gatekeeper still asks for right-click | |
| # → Open, exactly as before). Swap "-" for a Developer ID identity + add | |
| # notarization later for silent updates; see UPDATES.md. | |
| xcodebuild \ | |
| -scheme "$SCHEME" \ | |
| -project "$PRODUCT_NAME.xcodeproj" \ | |
| -configuration Release \ | |
| -derivedDataPath build \ | |
| -destination 'platform=macOS' \ | |
| CODE_SIGN_IDENTITY="-" \ | |
| CODE_SIGN_STYLE=Manual \ | |
| CODE_SIGNING_REQUIRED=NO \ | |
| CODE_SIGNING_ALLOWED=YES \ | |
| DEVELOPMENT_TEAM="" \ | |
| CURRENT_PROJECT_VERSION=${{ github.run_number }} \ | |
| clean build | |
| - name: Run Unit Tests | |
| run: | | |
| xcodebuild \ | |
| -scheme "$SCHEME" \ | |
| -project "$PRODUCT_NAME.xcodeproj" \ | |
| -destination 'platform=macOS' \ | |
| -resultBundlePath TestResults.xcresult \ | |
| CODE_SIGN_IDENTITY="-" \ | |
| CODE_SIGNING_REQUIRED=NO \ | |
| CODE_SIGNING_ALLOWED=NO \ | |
| DEVELOPMENT_TEAM="" \ | |
| test || true | |
| - name: Package app | |
| id: package | |
| run: | | |
| APP_PATH="build/Build/Products/Release/$PRODUCT_NAME.app" | |
| if [ ! -d "$APP_PATH" ]; then | |
| echo "❌ App not found at $APP_PATH" | |
| find build -name "*.app" -type d | |
| exit 1 | |
| fi | |
| # Get version info | |
| VERSION=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "$APP_PATH/Contents/Info.plist" 2>/dev/null || echo "1.0") | |
| BUILD=$(/usr/libexec/PlistBuddy -c "Print :CFBundleVersion" "$APP_PATH/Contents/Info.plist" 2>/dev/null || echo "1") | |
| # Alpha counter is PER VERSION: bumping MARKETING_VERSION restarts at alpha.1 | |
| git fetch --tags --force --quiet | |
| ALPHA_N=$(( $(git tag -l "v$VERSION-alpha.*" | wc -l | tr -d ' ') + 1 )) | |
| echo "VERSION=$VERSION" >> $GITHUB_ENV | |
| echo "BUILD=$BUILD" >> $GITHUB_ENV | |
| echo "PRERELEASE_VERSION=$VERSION-alpha.${ALPHA_N}" >> $GITHUB_ENV | |
| echo "PRERELEASE_TAG=v$VERSION-alpha.${ALPHA_N}" >> $GITHUB_ENV | |
| echo "VERSION=$VERSION" >> $GITHUB_OUTPUT | |
| echo "PRERELEASE_VERSION=$VERSION-alpha.${ALPHA_N}" >> $GITHUB_OUTPUT | |
| echo "PRERELEASE_TAG=v$VERSION-alpha.${ALPHA_N}" >> $GITHUB_OUTPUT | |
| # Create ZIP | |
| cd "build/Build/Products/Release" | |
| ditto -c -k --keepParent "$PRODUCT_NAME.app" "$PRODUCT_NAME-$VERSION.zip" | |
| mv "$PRODUCT_NAME-$VERSION.zip" "$GITHUB_WORKSPACE/" | |
| # Create DMG | |
| hdiutil create -volname "$PRODUCT_NAME" \ | |
| -srcfolder "$PRODUCT_NAME.app" \ | |
| -ov -format UDZO \ | |
| "$GITHUB_WORKSPACE/$PRODUCT_NAME-$VERSION.dmg" | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v5 | |
| with: | |
| name: TopPresenter-${{ env.VERSION }} | |
| path: | | |
| TopPresenter-*.zip | |
| TopPresenter-*.dmg | |
| retention-days: 30 | |
| # Unique pre-release on every push to main | |
| pre-release: | |
| name: Pre-release (Alpha) | |
| needs: build | |
| if: github.ref == 'refs/heads/main' && github.event_name == 'push' | |
| runs-on: macos-15 | |
| permissions: | |
| contents: write | |
| pages: write # deploy the appcast to Pages (no gh-pages branch) | |
| id-token: write # required by actions/deploy-pages | |
| environment: | |
| name: github-pages | |
| env: | |
| # Empty until you add the repo secret — the appcast steps then no-op (CI stays green). | |
| SPARKLE_PRIVATE_KEY: ${{ secrets.SPARKLE_PRIVATE_KEY }} | |
| steps: | |
| - name: Checkout (for appcast scripts) | |
| uses: actions/checkout@v5 | |
| - name: Download artifact | |
| uses: actions/download-artifact@v5 | |
| with: | |
| pattern: TopPresenter-* | |
| merge-multiple: true | |
| - name: Get short SHA | |
| run: echo "SHORT_SHA=${GITHUB_SHA::7}" >> $GITHUB_ENV | |
| - name: Create alpha pre-release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ needs.build.outputs.prerelease_tag }} | |
| name: "TopPresenter ${{ needs.build.outputs.prerelease_version }}" | |
| prerelease: true | |
| generate_release_notes: true | |
| body: | | |
| ### ⚡ Automatic Alpha Build | |
| **Version:** `${{ needs.build.outputs.prerelease_version }}` | |
| **Tag:** `${{ needs.build.outputs.prerelease_tag }}` | |
| **Commit:** `${{ github.sha }}` | |
| **Branch:** `main` | |
| **Built:** ${{ github.event.head_commit.timestamp }} | |
| > ⚠️ This is an unsigned build. To open on macOS: | |
| > Right-click the app → Open → Open (bypass Gatekeeper). | |
| > Or run: `xattr -cr TopPresenter.app` | |
| --- | |
| **Commit message:** ${{ github.event.head_commit.message }} | |
| files: | | |
| TopPresenter-*.zip | |
| TopPresenter-*.dmg | |
| - name: Build signed appcast (beta channel) | |
| if: ${{ env.SPARKLE_PRIVATE_KEY != '' }} | |
| env: | |
| ZIP: TopPresenter-${{ needs.build.outputs.version }}.zip | |
| DOWNLOAD_URL: https://github.com/${{ github.repository }}/releases/download/${{ needs.build.outputs.prerelease_tag }}/TopPresenter-${{ needs.build.outputs.version }}.zip | |
| SHORT_VERSION: ${{ needs.build.outputs.prerelease_version }} | |
| BUILD_VERSION: ${{ github.run_number }} | |
| CHANNEL: beta | |
| FEED_URL: https://robyrew.github.io/TopPresenter/appcast.xml | |
| NOTES: ${{ github.event.head_commit.message }} | |
| run: | | |
| chmod +x scripts/publish_appcast.sh | |
| scripts/publish_appcast.sh | |
| - name: Setup Pages | |
| if: ${{ env.SPARKLE_PRIVATE_KEY != '' }} | |
| uses: actions/configure-pages@v5 | |
| - name: Upload appcast artifact | |
| if: ${{ env.SPARKLE_PRIVATE_KEY != '' }} | |
| uses: actions/upload-pages-artifact@v3 | |
| with: | |
| path: site | |
| - name: Deploy appcast to Pages | |
| if: ${{ env.SPARKLE_PRIVATE_KEY != '' }} | |
| uses: actions/deploy-pages@v4 | |
| # Stable release on final version tags (v1.0.0, v1.1.0, etc.) | |
| release: | |
| name: Stable Release | |
| needs: build | |
| if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref_name, '-') | |
| runs-on: macos-15 | |
| permissions: | |
| contents: write | |
| pages: write # deploy the appcast to Pages (no gh-pages branch) | |
| id-token: write # required by actions/deploy-pages | |
| environment: | |
| name: github-pages | |
| env: | |
| SPARKLE_PRIVATE_KEY: ${{ secrets.SPARKLE_PRIVATE_KEY }} | |
| steps: | |
| - name: Checkout (for appcast scripts) | |
| uses: actions/checkout@v5 | |
| - name: Download artifact | |
| uses: actions/download-artifact@v5 | |
| with: | |
| pattern: TopPresenter-* | |
| merge-multiple: true | |
| - name: Create stable release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| name: "TopPresenter ${{ github.ref_name }}" | |
| generate_release_notes: true | |
| body: | | |
| ### 🎉 TopPresenter ${{ github.ref_name }} | |
| > ⚠️ This is an unsigned build. To open on macOS: | |
| > Right-click the app → Open → Open (bypass Gatekeeper). | |
| > Or run: `xattr -cr TopPresenter.app` | |
| files: | | |
| TopPresenter-*.zip | |
| TopPresenter-*.dmg | |
| - name: Build signed appcast (stable channel) | |
| if: ${{ env.SPARKLE_PRIVATE_KEY != '' }} | |
| env: | |
| ZIP: TopPresenter-${{ needs.build.outputs.version }}.zip | |
| DOWNLOAD_URL: https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/TopPresenter-${{ needs.build.outputs.version }}.zip | |
| SHORT_VERSION: ${{ needs.build.outputs.version }} | |
| BUILD_VERSION: ${{ github.run_number }} | |
| CHANNEL: "" | |
| FEED_URL: https://robyrew.github.io/TopPresenter/appcast.xml | |
| run: | | |
| chmod +x scripts/publish_appcast.sh | |
| scripts/publish_appcast.sh | |
| - name: Setup Pages | |
| if: ${{ env.SPARKLE_PRIVATE_KEY != '' }} | |
| uses: actions/configure-pages@v5 | |
| - name: Upload appcast artifact | |
| if: ${{ env.SPARKLE_PRIVATE_KEY != '' }} | |
| uses: actions/upload-pages-artifact@v3 | |
| with: | |
| path: site | |
| - name: Deploy appcast to Pages | |
| if: ${{ env.SPARKLE_PRIVATE_KEY != '' }} | |
| uses: actions/deploy-pages@v4 |