Skip to content

Release

Release #12

Workflow file for this run

# iOS Release Workflow
name: Release
on:
workflow_dispatch:
jobs:
release:
runs-on: macos-latest
permissions:
contents: write
defaults:
run:
working-directory: client
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Analyze commits and bump version
id: version
run: |
# Get last tag or use initial version
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
echo "Last tag: $LAST_TAG"
# Get commits since last tag
COMMITS=$(git log ${LAST_TAG}..HEAD --pretty=format:"%s" 2>/dev/null || git log --pretty=format:"%s")
# Determine bump type from conventional commits
BUMP="none"
if echo "$COMMITS" | grep -qE "^(feat|fix|refactor|perf|build)(\(.+\))?!:|BREAKING CHANGE:"; then
BUMP="major"
elif echo "$COMMITS" | grep -qE "^feat(\(.+\))?:"; then
BUMP="minor"
elif echo "$COMMITS" | grep -qE "^(fix|perf|refactor|build)(\(.+\))?:"; then
BUMP="patch"
fi
if [ "$BUMP" = "none" ]; then
echo "No conventional commits found. Skipping release."
echo "skip=true" >> $GITHUB_OUTPUT
exit 0
fi
# Extract current version
CURRENT=$(grep '^version:' pubspec.yaml | sed 's/version: //' | cut -d'+' -f1)
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT"
case "$BUMP" in
major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;;
minor) MINOR=$((MINOR + 1)); PATCH=0 ;;
patch) PATCH=$((PATCH + 1)) ;;
esac
NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}"
BUILD_NUMBER=$(date +%Y%m%d%H%M)
# Update pubspec.yaml
sed -i '' "s/^version: .*/version: ${NEW_VERSION}+${BUILD_NUMBER}/" pubspec.yaml
echo "version=${NEW_VERSION}" >> $GITHUB_OUTPUT
echo "build=${BUILD_NUMBER}" >> $GITHUB_OUTPUT
echo "bump=${BUMP}" >> $GITHUB_OUTPUT
echo "skip=false" >> $GITHUB_OUTPUT
echo "Bumping $BUMP: $CURRENT -> $NEW_VERSION"
- name: Skip if no release needed
if: steps.version.outputs.skip == 'true'
run: |
echo "No releasable commits found. Use conventional commits:"
echo " feat: ... (minor bump)"
echo " fix: ... (patch bump)"
echo " feat!: ... or BREAKING CHANGE: (major bump)"
exit 1
- uses: subosito/flutter-action@v2
with:
flutter-version: "3.38.5"
channel: "stable"
cache: true
- name: Install Apple certificate and provisioning profile
env:
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
# Create variables
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
# Import certificate and provisioning profile from secrets
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH
echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH
# Create temporary keychain
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 certificate to keychain
security import $CERTIFICATE_PATH -P "$P12_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
# Apply provisioning profile
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles
- name: Create env files
run: |
touch .env.local
cat > .env.prod << EOF
CONVEX_URL=${{ secrets.CONVEX_URL }}
CLERK_PUBLISHABLE_KEY=${{ secrets.CLERK_PUBLISHABLE_KEY }}
GOOGLE_PLACES_API_KEY=${{ secrets.GOOGLE_PLACES_API_KEY }}
EOF
- name: Install dependencies
run: flutter pub get
- name: Install CocoaPods
run: cd ios && pod install
- name: Build iOS
run: flutter build ios --release --no-codesign
- name: Build and upload with Fastlane
env:
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
APP_STORE_CONNECT_API_PRIVATE_KEY: ${{ secrets.APP_STORE_CONNECT_API_PRIVATE_KEY }}
run: |
cd ios
bundle install
bundle exec fastlane release
- name: Clean up keychain
if: always()
run: security delete-keychain $RUNNER_TEMP/app-signing.keychain-db
- name: Commit version bump
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add pubspec.yaml
git commit -m "chore(release): v${{ steps.version.outputs.version }}"
git push
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ steps.version.outputs.version }}
name: v${{ steps.version.outputs.version }}
generate_release_notes: true