chore(ios): bump version to v1.1.0-beta.20260331.1 #52
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 iOS | |
| on: | |
| push: | |
| tags: ["v*"] | |
| permissions: | |
| contents: write | |
| jobs: | |
| # ── Extract version, changelog & release type ─────────────────── | |
| prepare: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.meta.outputs.version }} | |
| marketing_version: ${{ steps.meta.outputs.marketing_version }} | |
| build_number: ${{ steps.meta.outputs.build_number }} | |
| changelog: ${{ steps.changelog.outputs.changelog }} | |
| is_beta: ${{ steps.meta.outputs.is_beta }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - id: meta | |
| run: | | |
| TAG="${{ github.ref_name }}" | |
| # Strip leading 'v' | |
| FULL="${TAG#v}" | |
| # Extract marketing version (X.Y.Z only, for Apple) | |
| MARKETING=$(echo "$FULL" | grep -oE '^[0-9]+\.[0-9]+\.[0-9]+') | |
| # Extract build number: YYYYMMDD + daily sequence → YYYYMMDDN | |
| # e.g. 0.0.1-beta.20260319.13 → 2026031913 | |
| if echo "$FULL" | grep -qE '20[0-9]{6}\.[0-9]+'; then | |
| DATE_PART=$(echo "$FULL" | grep -oE '20[0-9]{6}') | |
| SEQ_PART=$(echo "$FULL" | grep -oE '20[0-9]{6}\.([0-9]+)' | sed 's/.*\.//') | |
| BUILD="${DATE_PART}$(printf '%02d' "$SEQ_PART")" | |
| elif echo "$FULL" | grep -qE '20[0-9]{6}'; then | |
| BUILD=$(echo "$FULL" | grep -oE '20[0-9]{6}') | |
| else | |
| BUILD=$(date +%Y%m%d)1 | |
| fi | |
| # Full version string for display (e.g. 0.0.1-beta.20260319.5) | |
| VERSION="$FULL" | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "marketing_version=$MARKETING" >> "$GITHUB_OUTPUT" | |
| echo "build_number=$BUILD" >> "$GITHUB_OUTPUT" | |
| if echo "$TAG" | grep -q "beta"; then | |
| echo "is_beta=true" >> "$GITHUB_OUTPUT" | |
| elif echo "$TAG" | grep -q "alpha"; then | |
| echo "is_beta=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "is_beta=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - id: changelog | |
| run: | | |
| PREV_TAG=$(git tag --sort=-creatordate | head -2 | tail -1) | |
| if [ -z "$PREV_TAG" ] || [ "$PREV_TAG" = "${{ github.ref_name }}" ]; then | |
| LOG=$(git log --oneline -10 --pretty=format:"- %s") | |
| else | |
| LOG=$(git log --oneline "${PREV_TAG}..${{ github.ref_name }}" --pretty=format:"- %s") | |
| fi | |
| { | |
| echo "changelog<<CHANGELOG_EOF" | |
| echo "$LOG" | |
| echo "CHANGELOG_EOF" | |
| } >> "$GITHUB_OUTPUT" | |
| # ── iOS IPA (cloud signing + TestFlight) ───────────────────── | |
| build-ios: | |
| runs-on: macos-26 | |
| needs: prepare | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Select Xcode 26 | |
| uses: maxim-lobanov/setup-xcode@v1 | |
| with: | |
| xcode-version: '26' | |
| - name: Download iOS platform | |
| run: xcodebuild -downloadPlatform iOS | |
| - name: Install Apple certificates | |
| env: | |
| APPLE_DIST_P12: ${{ secrets.APPLE_CERTIFICATE_P12 }} | |
| CERT_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} | |
| run: | | |
| KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db | |
| KEYCHAIN_PASSWORD=$(openssl rand -hex 16) | |
| security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" | |
| security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH" | |
| security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" | |
| # Import Distribution certificate | |
| echo "$APPLE_DIST_P12" | base64 -d > $RUNNER_TEMP/dist.p12 | |
| security import $RUNNER_TEMP/dist.p12 \ | |
| -P "$CERT_PASSWORD" -A -t cert -f pkcs12 -k "$KEYCHAIN_PATH" | |
| security set-key-partition-list -S apple-tool:,apple: \ | |
| -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" | |
| security list-keychain -d user -s "$KEYCHAIN_PATH" | |
| # Verify | |
| security find-identity -v -p codesigning "$KEYCHAIN_PATH" | |
| - name: Install provisioning profile | |
| env: | |
| PROVISIONING_PROFILE: ${{ secrets.IOS_PROVISIONING_PROFILE }} | |
| run: | | |
| mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles | |
| echo "$PROVISIONING_PROFILE" | base64 -d \ | |
| > ~/Library/MobileDevice/Provisioning\ Profiles/ClawChat_App_Store.mobileprovision | |
| - name: Setup App Store Connect API Key | |
| env: | |
| ASC_API_KEY_P8: ${{ secrets.ASC_API_KEY_P8 }} | |
| ASC_API_KEY_ID: ${{ secrets.ASC_API_KEY_ID }} | |
| run: | | |
| mkdir -p ~/private_keys | |
| echo "$ASC_API_KEY_P8" | base64 -d > ~/private_keys/AuthKey_${ASC_API_KEY_ID}.p8 | |
| - name: Resolve Swift packages | |
| working-directory: ios | |
| run: | | |
| xcodebuild -resolvePackageDependencies \ | |
| -project ClawOS.xcodeproj \ | |
| -scheme ClawOS | |
| - name: Build iOS archive | |
| working-directory: ios | |
| env: | |
| MARKETING_VER: ${{ needs.prepare.outputs.marketing_version }} | |
| BUILD: ${{ needs.prepare.outputs.build_number }} | |
| run: | | |
| xcodebuild -project ClawOS.xcodeproj \ | |
| -scheme ClawOS \ | |
| -configuration Release \ | |
| -destination 'generic/platform=iOS' \ | |
| -archivePath build/ClawChat.xcarchive \ | |
| archive \ | |
| MARKETING_VERSION="${MARKETING_VER}" \ | |
| CURRENT_PROJECT_VERSION="${BUILD}" \ | |
| CODE_SIGN_STYLE=Manual \ | |
| CODE_SIGN_IDENTITY="Apple Distribution" \ | |
| DEVELOPMENT_TEAM="LBN3W98T26" \ | |
| PROVISIONING_PROFILE_SPECIFIER="ClawChat App Store" \ | |
| SWIFT_STRICT_CONCURRENCY=minimal | |
| - name: Export IPA | |
| working-directory: ios | |
| env: | |
| ASC_API_KEY_ID: ${{ secrets.ASC_API_KEY_ID }} | |
| ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }} | |
| run: | | |
| xcodebuild -exportArchive \ | |
| -archivePath build/ClawChat.xcarchive \ | |
| -exportOptionsPlist ExportOptions.plist \ | |
| -exportPath build/ipa \ | |
| -authenticationKeyPath ~/private_keys/AuthKey_${ASC_API_KEY_ID}.p8 \ | |
| -authenticationKeyID "$ASC_API_KEY_ID" \ | |
| -authenticationKeyIssuerID "$ASC_ISSUER_ID" | |
| - name: Rename IPA | |
| run: | | |
| VERSION="${{ needs.prepare.outputs.version }}" | |
| BUILD="${{ needs.prepare.outputs.build_number }}" | |
| IPA_SRC=$(find ios/build/ipa -name '*.ipa' | head -1) | |
| cp "$IPA_SRC" "clawchat-${VERSION}+${BUILD}.ipa" | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: ios-ipa | |
| path: "clawchat-*.ipa" | |
| - name: Upload to TestFlight | |
| env: | |
| ASC_API_KEY_ID: ${{ secrets.ASC_API_KEY_ID }} | |
| ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }} | |
| run: | | |
| VERSION="${{ needs.prepare.outputs.version }}" | |
| BUILD="${{ needs.prepare.outputs.build_number }}" | |
| xcrun altool --upload-app --type ios \ | |
| -f "clawchat-${VERSION}+${BUILD}.ipa" \ | |
| --apiKey "$ASC_API_KEY_ID" \ | |
| --apiIssuer "$ASC_ISSUER_ID" | |
| # ── GitHub Release ──────────────────────────────────────────── | |
| release: | |
| runs-on: ubuntu-latest | |
| needs: [prepare, build-ios] | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: ios-ipa | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| name: "v${{ needs.prepare.outputs.version }} (Build ${{ needs.prepare.outputs.build_number }})" | |
| prerelease: ${{ needs.prepare.outputs.is_beta == 'true' }} | |
| body: | | |
| ## What's Changed | |
| ${{ needs.prepare.outputs.changelog }} | |
| --- | |
| - TestFlight: uploaded automatically | |
| files: | | |
| clawchat-*.ipa | |
| # ── Feishu Notification ─────────────────────────────────────── | |
| notify: | |
| runs-on: ubuntu-latest | |
| needs: [prepare, build-ios] | |
| if: ${{ always() && needs.build-ios.result == 'success' }} | |
| steps: | |
| - name: Notify Feishu | |
| env: | |
| FEISHU_WEBHOOK: ${{ secrets.FEISHU_WEBHOOK_URL }} | |
| VERSION: ${{ needs.prepare.outputs.version }} | |
| BUILD: ${{ needs.prepare.outputs.build_number }} | |
| CHANGELOG: ${{ needs.prepare.outputs.changelog }} | |
| IS_BETA: ${{ needs.prepare.outputs.is_beta }} | |
| run: | | |
| if [ -z "$FEISHU_WEBHOOK" ]; then | |
| echo "FEISHU_WEBHOOK_URL not set, skipping notification" | |
| exit 0 | |
| fi | |
| if [ "$IS_BETA" = "true" ]; then | |
| LABEL="Beta" | |
| HEADER_COLOR="orange" | |
| else | |
| LABEL="正式版" | |
| HEADER_COLOR="green" | |
| fi | |
| TITLE="ClawChat v${VERSION} (Build ${BUILD}) — ${LABEL}" | |
| CONTENT=$(printf '**What'\''s Changed:**\n%s' "$CHANGELOG") | |
| PAYLOAD=$(jq -n \ | |
| --arg title "$TITLE" \ | |
| --arg changelog "$CONTENT" \ | |
| --arg color "$HEADER_COLOR" \ | |
| '{ | |
| msg_type: "interactive", | |
| card: { | |
| header: { | |
| title: { tag: "plain_text", content: $title }, | |
| template: $color | |
| }, | |
| elements: [ | |
| { tag: "markdown", content: $changelog }, | |
| { tag: "hr" }, | |
| { | |
| tag: "action", | |
| actions: [ | |
| { | |
| tag: "button", | |
| text: { tag: "plain_text", content: "iOS TestFlight" }, | |
| url: "https://testflight.apple.com/join/PLACEHOLDER", | |
| type: "primary" | |
| } | |
| ] | |
| } | |
| ] | |
| } | |
| }') | |
| curl -s -X POST "$FEISHU_WEBHOOK" \ | |
| -H "Content-Type: application/json" \ | |
| -d "$PAYLOAD" |