Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 177 additions & 22 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,30 +1,185 @@
name: Gradle CI
name: Build

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
branches: [main]
workflow_dispatch:
inputs:
create_dev_build:
description: 'Create development build'
required: false
default: false
type: boolean

concurrency:
group: build-${{ github.ref }}
cancel-in-progress: true # Cancel previous builds

jobs:
build:
test:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4

- name: Setup Java
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: gradle

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

- name: Run Tests
run: ./gradlew allTests --stacktrace --continue

build-verification:
needs: test
runs-on: macos-latest
steps:
- uses: actions/checkout@v4

- name: Setup Java
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: gradle

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

- name: Build All Modules
run: ./gradlew build --stacktrace

- name: Build XCFramework
run: ./gradlew :umbrella:createXCFramework --stacktrace

build-dev:
needs: [test, build-verification]
runs-on: macos-latest
if: github.event.inputs.create_dev_build == 'true'
outputs:
build-version: ${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Java
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: gradle

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

- name: Generate Build Version
id: version
run: |
# Use timestamp + short SHA for dev builds
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
SHORT_SHA=${GITHUB_SHA:0:7}
BUILD_VERSION="dev-$TIMESTAMP-$SHORT_SHA"
echo "version=$BUILD_VERSION" >> $GITHUB_OUTPUT
echo "📦 Build version: $BUILD_VERSION"

- name: Build XCFramework
run: ./gradlew :umbrella:createXCFramework

- name: Prepare Distribution
run: |
cd umbrella/build/XCFrameworks/release
zip -r QuranSync-${{ steps.version.outputs.version }}.xcframework.zip QuranSync.xcframework

- name: Calculate Checksum
id: checksum
run: |
CHECKSUM=$(swift package compute-checksum umbrella/build/XCFrameworks/release/QuranSync-${{ steps.version.outputs.version }}.xcframework.zip)
echo "checksum=$CHECKSUM" >> $GITHUB_OUTPUT

- name: Update Package.swift for Dev Build
run: |
# Update Package.swift directly with dev build info
sed -i '' "s/{VERSION}/${{ steps.version.outputs.version }}/g" Package.swift
sed -i '' "s/{CHECKSUM_TO_BE_REPLACED_BY_CI}/${{ steps.checksum.outputs.checksum }}/g" Package.swift

- name: Commit Package.swift for Dev Build
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add Package.swift
git commit -m "chore: update Package.swift for dev build ${{ steps.version.outputs.version }}"
git push origin main

- name: Create Development Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.version.outputs.version }}
name: "Development Build ${{ steps.version.outputs.version }}"
prerelease: true
files: |
umbrella/build/XCFrameworks/release/QuranSync-${{ steps.version.outputs.version }}.xcframework.zip
body: |
## 🚧 Development Build

**Commit:** ${{ github.sha }}
**Branch:** ${{ github.ref_name }}
**Checksum:** `${{ steps.checksum.outputs.checksum }}`

### Usage (SPM)
Package.swift has been automatically updated for this dev build:
```swift
.package(url: "https://github.com/quran/mobile-sync", exact: "${{ steps.version.outputs.version }}")
```

### Alternative: Manual Binary Target
If you prefer manual control:
```swift
.binaryTarget(
name: "QuranSync",
url: "https://github.com/quran/mobile-sync/releases/download/${{ steps.version.outputs.version }}/QuranSync-${{ steps.version.outputs.version }}.xcframework.zip",
checksum: "${{ steps.checksum.outputs.checksum }}"
)
```

### ⚠️ Important Warnings
- **This is a development build** - use stable releases for production
- **Dev builds may be deleted without notice** - we only keep the 3 most recent
- **Package.swift will be overwritten** by the next dev build or release
- **No stability guarantees** - APIs may change between dev builds
- **For testing only** - not recommended for App Store submissions
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

cleanup-old-dev-builds:
needs: build-dev
runs-on: ubuntu-latest
if: github.event.inputs.create_dev_build == 'true'
steps:
- uses: actions/checkout@v5

- name: Set up JDK 17
uses: actions/setup-java@v5
with:
java-version: '17'
distribution: 'temurin'
cache: gradle

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

- name: Clean project
run: ./gradlew clean

- name: Run tests
run: ./gradlew allTests --stacktrace --continue
- name: Cleanup Old Dev Builds
uses: actions/github-script@v7
with:
script: |
const { data: releases } = await github.rest.repos.listReleases({
owner: context.repo.owner,
repo: context.repo.repo,
});

// Keep last 3 dev builds, delete older ones
const devBuilds = releases
.filter(release => release.tag_name.startsWith('dev-'))
.sort((a, b) => new Date(b.created_at) - new Date(a.created_at))
.slice(3); // Skip first 3 (keep them)

for (const release of devBuilds) {
console.log(`Deleting old dev build: ${release.tag_name}`);
await github.rest.repos.deleteRelease({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: release.id,
});
}
176 changes: 176 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
name: Release

on:
workflow_dispatch:
inputs:
version_bump:
description: 'Version bump type'
required: true
default: 'patch'
type: choice
options:
- patch
- minor
- major
prerelease:
description: 'Is this a prerelease?'
required: false
default: false
type: boolean

concurrency:
group: release
cancel-in-progress: false # Don't cancel releases

jobs:
release:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Setup Java
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: gradle

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

- name: Configure Git
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

- name: Get Current Version
id: current_version
run: |
# Get latest tag, default to 0.0.0 if none exists
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
CURRENT_VERSION=${LATEST_TAG#v} # Remove 'v' prefix
echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
echo "📋 Current version: $CURRENT_VERSION"

- name: Calculate Next Version
id: next_version
run: |
CURRENT="${{ steps.current_version.outputs.version }}"
BUMP="${{ github.event.inputs.version_bump }}"

# Split version into parts
IFS='.' read -ra VERSION_PARTS <<< "$CURRENT"
MAJOR=${VERSION_PARTS[0]:-0}
MINOR=${VERSION_PARTS[1]:-0}
PATCH=${VERSION_PARTS[2]:-0}

# Calculate new version based on bump type
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"
echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "🚀 Next version: $NEW_VERSION"

- name: Run Tests
run: ./gradlew allTests

- name: Build XCFramework
run: ./gradlew :umbrella:createXCFramework

- name: Prepare Distribution
run: |
cd umbrella/build/XCFrameworks/release
zip -r QuranSync.xcframework.zip QuranSync.xcframework

- name: Calculate Checksum
id: checksum
run: |
CHECKSUM=$(swift package compute-checksum umbrella/build/XCFrameworks/release/QuranSync.xcframework.zip)
echo "checksum=$CHECKSUM" >> $GITHUB_OUTPUT

- name: Update Package.swift
run: |
sed -i '' "s/{VERSION}/v${{ steps.next_version.outputs.version }}/g" Package.swift
sed -i '' "s/{CHECKSUM_TO_BE_REPLACED_BY_CI}/${{ steps.checksum.outputs.checksum }}/g" Package.swift

- name: Generate Release Notes
id: release_notes
run: |
# Get commits since last release
LAST_TAG="${{ steps.current_version.outputs.version }}"
if [ "$LAST_TAG" = "0.0.0" ]; then
COMMITS=$(git log --pretty=format:"- %s" --reverse)
else
COMMITS=$(git log v$LAST_TAG..HEAD --pretty=format:"- %s" --reverse)
fi

# Create release notes
cat > release_notes.md << EOF
## What's Changed

$COMMITS

## Installation

### Swift Package Manager
Add to your \`Package.swift\`:
\`\`\`swift
dependencies: [
.package(url: "https://github.com/quran/mobile-sync", from: "${{ steps.next_version.outputs.version }}")
]
\`\`\`

### Xcode
1. File → Add Package Dependencies
2. Enter: \`https://github.com/quran/mobile-sync\`
3. Select version: \`${{ steps.next_version.outputs.version }}\`
EOF

- name: Commit Package.swift Updates
run: |
git add Package.swift
git commit -m "chore: update Package.swift for v${{ steps.next_version.outputs.version }}" || exit 0

- name: Create Tag
run: |
git tag -a "v${{ steps.next_version.outputs.version }}" -m "Release v${{ steps.next_version.outputs.version }}"

- name: Push Changes
run: |
git push origin main
git push origin "v${{ steps.next_version.outputs.version }}"

- name: Create Release
uses: softprops/action-gh-release@v1
with:
tag_name: "v${{ steps.next_version.outputs.version }}"
name: "QuranSync v${{ steps.next_version.outputs.version }}"
prerelease: ${{ github.event.inputs.prerelease }}
files: |
umbrella/build/XCFrameworks/release/QuranSync.xcframework.zip
body_path: release_notes.md
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Summary
run: |
echo "✅ Released QuranSync v${{ steps.next_version.outputs.version }}"
echo "🏷️ Tag: v${{ steps.next_version.outputs.version }}"
echo "📦 Asset: QuranSync.xcframework.zip"
echo "🔗 Release: https://github.com/quran/mobile-sync/releases/tag/v${{ steps.next_version.outputs.version }}"
Loading