Skip to content

chore(ios): bump version to v1.1.0-beta.20260331.1 #52

chore(ios): bump version to v1.1.0-beta.20260331.1

chore(ios): bump version to v1.1.0-beta.20260331.1 #52

Workflow file for this run

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"