Release #17
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: | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "Marketing version and tag (for example: 0.3.0)" | |
| required: true | |
| type: string | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: release | |
| cancel-in-progress: false | |
| jobs: | |
| release: | |
| runs-on: macos-26 | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| ref: main | |
| fetch-depth: 0 | |
| - name: Validate version | |
| env: | |
| VERSION: ${{ inputs.version }} | |
| run: | | |
| if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| echo "::error::Version must use the x.y.z format." | |
| exit 1 | |
| fi | |
| if git rev-parse --verify --quiet "refs/tags/$VERSION"; then | |
| echo "::error::Tag $VERSION already exists." | |
| exit 1 | |
| fi | |
| - name: Update marketing version | |
| env: | |
| VERSION: ${{ inputs.version }} | |
| run: | | |
| ruby <<'RUBY' | |
| path = "TrackpadAir.xcodeproj/project.pbxproj" | |
| project = File.read(path) | |
| version = ENV.fetch("VERSION") | |
| updated_configurations = 0 | |
| updated_project = project.gsub( | |
| /(\w+ \/\* (?:Debug|Release) \*\/ = \{\n\s+isa = XCBuildConfiguration;.*?\n\s+\};)/m | |
| ) do |configuration| | |
| unless configuration.include?("PRODUCT_BUNDLE_IDENTIFIER = com.TrackpadAir;") | |
| next configuration | |
| end | |
| updated_configuration = configuration.sub( | |
| /MARKETING_VERSION = [^;]+;/, | |
| "MARKETING_VERSION = #{version};" | |
| ) | |
| if updated_configuration == configuration | |
| abort "MARKETING_VERSION was not found in an app build configuration" | |
| end | |
| updated_configurations += 1 | |
| updated_configuration | |
| end | |
| unless updated_configurations == 2 | |
| abort "Expected to update 2 app build configurations, updated #{updated_configurations}" | |
| end | |
| File.write(path, updated_project) | |
| RUBY | |
| - name: Push version and tag | |
| env: | |
| VERSION: ${{ inputs.version }} | |
| run: | | |
| if git diff --quiet -- TrackpadAir.xcodeproj/project.pbxproj; then | |
| echo "::error::Marketing version is already $VERSION." | |
| exit 1 | |
| fi | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git add TrackpadAir.xcodeproj/project.pbxproj | |
| git commit -m "Bump version to $VERSION" | |
| git push origin HEAD:main | |
| git tag "$VERSION" | |
| git push origin "$VERSION" | |
| - name: Import signing certificate | |
| env: | |
| CERTIFICATE_PASSWORD: ${{ secrets.CERTIFICATE_PASSWORD }} | |
| CERTIFICATE_P12: ${{ secrets.CERTIFICATE_P12 }} | |
| run: | | |
| KEYCHAIN_PATH="$RUNNER_TEMP/build.keychain-db" | |
| CERTIFICATE_PATH="$RUNNER_TEMP/certificate.p12" | |
| printf '%s' "$CERTIFICATE_P12" | base64 --decode > "$CERTIFICATE_PATH" | |
| security create-keychain -p runner "$KEYCHAIN_PATH" | |
| security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH" | |
| security unlock-keychain -p runner "$KEYCHAIN_PATH" | |
| security import "$CERTIFICATE_PATH" \ | |
| -k "$KEYCHAIN_PATH" \ | |
| -P "$CERTIFICATE_PASSWORD" \ | |
| -T /usr/bin/codesign | |
| security set-key-partition-list \ | |
| -S apple-tool:,apple: \ | |
| -s \ | |
| -k runner \ | |
| "$KEYCHAIN_PATH" | |
| security list-keychains -d user -s "$KEYCHAIN_PATH" | |
| - name: Run tests | |
| run: | | |
| xcodebuild test \ | |
| -project TrackpadAir.xcodeproj \ | |
| -scheme TrackpadAir \ | |
| -destination 'platform=macOS' \ | |
| CODE_SIGNING_ALLOWED=NO | |
| - name: Archive app | |
| run: | | |
| xcodebuild archive \ | |
| -project TrackpadAir.xcodeproj \ | |
| -scheme TrackpadAir \ | |
| -archivePath "$RUNNER_TEMP/TrackpadAir.xcarchive" | |
| - name: Notarize app | |
| env: | |
| APP_STORE_CONNECT_API_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_ISSUER_ID }} | |
| APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }} | |
| APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }} | |
| run: | | |
| APP_PATH="$RUNNER_TEMP/TrackpadAir.xcarchive/Products/Applications/TrackpadAir.app" | |
| NOTARIZATION_ZIP="$RUNNER_TEMP/TrackpadAir-notarization.zip" | |
| API_KEY_PATH="$RUNNER_TEMP/AuthKey_$APP_STORE_CONNECT_API_KEY_ID.p8" | |
| printf '%s' "$APP_STORE_CONNECT_API_KEY" > "$API_KEY_PATH" | |
| ditto -c -k --keepParent "$APP_PATH" "$NOTARIZATION_ZIP" | |
| xcrun notarytool submit "$NOTARIZATION_ZIP" \ | |
| --key "$API_KEY_PATH" \ | |
| --key-id "$APP_STORE_CONNECT_API_KEY_ID" \ | |
| --issuer "$APP_STORE_CONNECT_API_ISSUER_ID" \ | |
| --wait | |
| xcrun stapler staple "$APP_PATH" | |
| xcrun stapler validate "$APP_PATH" | |
| spctl --assess --type execute --verbose=4 "$APP_PATH" | |
| - name: Package app | |
| run: | | |
| ditto -c -k --keepParent \ | |
| "$RUNNER_TEMP/TrackpadAir.xcarchive/Products/Applications/TrackpadAir.app" \ | |
| TrackpadAir.app.zip | |
| - name: Create GitHub release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| VERSION: ${{ inputs.version }} | |
| run: | | |
| gh release create "$VERSION" \ | |
| TrackpadAir.app.zip \ | |
| --generate-notes \ | |
| --verify-tag | |
| - name: Clean up keychain | |
| if: always() | |
| run: security delete-keychain "$RUNNER_TEMP/build.keychain-db" || true |