Skip to content

Make App Screenshots collapsible & add Back to Top button at bottom of README #33

Make App Screenshots collapsible & add Back to Top button at bottom of README

Make App Screenshots collapsible & add Back to Top button at bottom of README #33

Workflow file for this run

name: 🚀 Automated APK Release
on:
pull_request:
types: [closed, labeled]
branches: [main]
permissions:
contents: write # For pushing commits and tags
pull-requests: read # For reading PR information
jobs:
check-release-trigger:
if: github.event.pull_request.merged == true || (github.event.action == 'labeled' && github.event.pull_request.merged == true)
runs-on: ubuntu-latest
outputs:
should-release: ${{ steps.check.outputs.should-release }}
version-bump: ${{ steps.check.outputs.version-bump }}
release-notes: ${{ steps.check.outputs.release-notes }}
steps:
- name: Check Release Trigger
id: check
run: |
# Check if PR has release label or [release] in title
LABELS="${{ join(github.event.pull_request.labels.*.name, ' ') }}"
TITLE="${{ github.event.pull_request.title }}"
PR_BODY="${{ github.event.pull_request.body }}"
ACTION="${{ github.event.action }}"
echo "Action: $ACTION"
echo "Checking labels: $LABELS"
echo "Checking title: $TITLE"
echo "PR merged: ${{ github.event.pull_request.merged }}" # Determine version bump type from commit messages and PR title
VERSION_BUMP="patch"
if [[ "$TITLE" == *"[major]"* ]] || [[ "$TITLE" == *"BREAKING CHANGE"* ]]; then
VERSION_BUMP="major"
elif [[ "$TITLE" == *"[minor]"* ]] || [[ "$TITLE" == *"feat"* ]] || [[ "$TITLE" == *"feature"* ]]; then
VERSION_BUMP="minor"
fi
# Check if should release
SHOULD_RELEASE=false
if [[ "$LABELS" == *"release"* ]] || [[ "$TITLE" == *"[release]"* ]]; then
SHOULD_RELEASE=true
fi
# Special handling for label events - check if release label was just added
if [[ "$ACTION" == "labeled" ]]; then
ADDED_LABEL="${{ github.event.label.name }}"
echo "Label added: $ADDED_LABEL"
if [[ "$ADDED_LABEL" != "release" ]]; then
echo "Label '$ADDED_LABEL' is not a release trigger, skipping..."
SHOULD_RELEASE=false
fi
fi
# Prepare release notes from PR body
RELEASE_NOTES=$(echo "$PR_BODY" | head -n 20)
echo "should-release=$SHOULD_RELEASE" >> $GITHUB_OUTPUT
echo "version-bump=$VERSION_BUMP" >> $GITHUB_OUTPUT
echo "release-notes<<EOF" >> $GITHUB_OUTPUT
echo "$RELEASE_NOTES" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
build-and-release:
needs: check-release-trigger
if: needs.check-release-trigger.outputs.should-release == 'true'
runs-on: ubuntu-latest
steps:
- name: 🏗️ Checkout Repository
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
- name: 🔍 Check for Existing Release
id: check-existing
run: |
# Get current version first
CURRENT_VERSION=$(node -p "require('./package.json').version")
BUMP_TYPE="${{ needs.check-release-trigger.outputs.version-bump }}"
# Parse current version
IFS='.' read -ra VERSION_PARTS <<< "$CURRENT_VERSION"
MAJOR=${VERSION_PARTS[0]}
MINOR=${VERSION_PARTS[1]}
PATCH=${VERSION_PARTS[2]}
# Calculate new version
case $BUMP_TYPE 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}"
# Check if tag already exists
if git rev-parse "v$NEW_VERSION" >/dev/null 2>&1; then
echo "⚠️ Tag v$NEW_VERSION already exists!"
echo "existing=true" >> $GITHUB_OUTPUT
# Check if GitHub release exists
RELEASE_EXISTS=$(gh release view "v$NEW_VERSION" >/dev/null 2>&1 && echo "true" || echo "false")
echo "github-release-exists=$RELEASE_EXISTS" >> $GITHUB_OUTPUT
echo "skip-build=true" >> $GITHUB_OUTPUT
else
echo "✅ No existing release found for v$NEW_VERSION"
echo "existing=false" >> $GITHUB_OUTPUT
echo "github-release-exists=false" >> $GITHUB_OUTPUT
echo "skip-build=false" >> $GITHUB_OUTPUT
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: 🔧 Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'yarn'
- name: 💾 Cache node_modules
uses: actions/cache@v4
with:
path: |
node_modules
~/.cache/yarn
key: ${{ runner.os }}-yarn-${{ hashFiles('**/package.json') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: ☕ Setup Java JDK
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: '17'
- name: � Install Dependencies
run: |
echo "Installing dependencies with Yarn..."
# Check if yarn.lock exists
if [ -f "yarn.lock" ]; then
echo "yarn.lock found, using frozen lockfile"
yarn install --frozen-lockfile
else
echo "No yarn.lock found, generating new one"
yarn install
fi
# Verify React Native plugin is installed
echo "Checking for React Native Gradle plugin..."
if [ -d "node_modules/@react-native/gradle-plugin" ]; then
echo "✅ React Native Gradle plugin found"
ls -la node_modules/@react-native/gradle-plugin
else
echo "❌ React Native Gradle plugin NOT found"
echo "Available @react-native packages:"
ls -la node_modules/@react-native/ || echo "No @react-native directory found"
fi
yarn list --pattern "@react-native/gradle-plugin" # List specific package
- name: �🔍 Get Current Version
id: current-version
run: |
CURRENT_VERSION=$(node -p "require('./package.json').version")
echo "current=$CURRENT_VERSION" >> $GITHUB_OUTPUT
echo "Current version: $CURRENT_VERSION"
- name: 📈 Calculate New Version
id: new-version
run: |
CURRENT="${{ steps.current-version.outputs.current }}"
BUMP_TYPE="${{ needs.check-release-trigger.outputs.version-bump }}"
# Parse current version
IFS='.' read -ra VERSION_PARTS <<< "$CURRENT"
MAJOR=${VERSION_PARTS[0]}
MINOR=${VERSION_PARTS[1]}
PATCH=${VERSION_PARTS[2]}
# Calculate new version based on bump type
case $BUMP_TYPE 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 "new=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "New version: $NEW_VERSION"
- name: 📝 Update Version in Files
if: steps.check-existing.outputs.skip-build == 'false'
run: |
NEW_VERSION="${{ steps.new-version.outputs.new }}"
# Update package.json
yarn version --new-version $NEW_VERSION --no-git-tag-version
# Update Android versionName and versionCode
VERSION_CODE=$(grep "versionCode" android/app/build.gradle | grep -o '[0-9]\+')
NEW_VERSION_CODE=$((VERSION_CODE + 1))
sed -i "s/versionCode $VERSION_CODE/versionCode $NEW_VERSION_CODE/" android/app/build.gradle
sed -i "s/versionName \".*\"/versionName \"$NEW_VERSION\"/" android/app/build.gradle
echo "Updated version to $NEW_VERSION (code: $NEW_VERSION_CODE)"
- name: ⚠️ Release Already Exists
if: steps.check-existing.outputs.skip-build == 'true'
run: |
echo "## ⚠️ Release Already Exists"
echo "A release for this version already exists. Skipping build to prevent duplicates."
echo "If you need to rebuild, please:"
echo "1. Delete the existing tag and release, OR"
echo "2. Increment the version manually and re-trigger"
- name: 🔑 Setup Keystore
if: steps.check-existing.outputs.skip-build == 'false'
run: |
# Create keystore from secrets
echo "${{ secrets.RELEASE_KEYSTORE_BASE64 }}" | base64 -d > android/app/release-key.jks
# Create keystore.properties
cat > android/keystore.properties << EOF
storeFile=release-key.jks
storePassword=${{ secrets.RELEASE_STORE_PASSWORD }}
keyAlias=${{ secrets.RELEASE_KEY_ALIAS }}
keyPassword=${{ secrets.RELEASE_KEY_PASSWORD }}
EOF
- name: � Generate React Native Codegen
if: steps.check-existing.outputs.skip-build == 'false'
run: |
echo "🔧 Generating React Native Codegen artifacts..."
# Generate codegen for React Native libraries
cd android
chmod +x gradlew
# Clean first to ensure fresh codegen
./gradlew clean
# Generate codegen artifacts for all autolinked libraries
echo "Generating codegen artifacts from schema..."
./gradlew generateCodegenArtifactsFromSchema --stacktrace --debug || {
echo "⚠️ Primary codegen generation failed, trying alternative approach..."
# Try generating codegen for individual packages
echo "Generating codegen for individual packages..."
./gradlew :generateCodegenSchemaFromJavaScript --stacktrace || echo "Schema generation completed with warnings"
}
# Verify codegen directories were created
echo "Verifying codegen artifacts..."
find ../node_modules -name "codegen" -type d | head -10
echo "✅ Codegen generation completed"
- name: 🔨 Build Release APK
if: steps.check-existing.outputs.skip-build == 'false'
run: |
# Ensure node_modules exists and has React Native plugin
if [ ! -d "node_modules/@react-native/gradle-plugin" ]; then
echo "❌ React Native Gradle plugin not found in node_modules!"
echo "Available in node_modules/@react-native/:"
ls -la node_modules/@react-native/ || echo "Directory not found"
exit 1
fi
# Build Android APK with codegen artifacts
echo "Building Android APK with codegen..."
cd android
chmod +x gradlew
./gradlew assembleRelease --stacktrace
# Verify APK was created
if [ ! -f "app/build/outputs/apk/release/app-release.apk" ]; then
echo "❌ APK build failed!"
exit 1
fi
# Rename APK with version
NEW_VERSION="${{ steps.new-version.outputs.new }}"
cp app/build/outputs/apk/release/app-release.apk app/build/outputs/apk/release/ScheduleX-v${NEW_VERSION}.apk
# Get APK info
APK_SIZE=$(du -h app/build/outputs/apk/release/ScheduleX-v${NEW_VERSION}.apk | cut -f1)
echo "✅ APK built successfully! Size: $APK_SIZE"
- name: 🏷️ Create Git Tag
if: steps.check-existing.outputs.skip-build == 'false'
run: |
NEW_VERSION="${{ steps.new-version.outputs.new }}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add package.json android/app/build.gradle
git commit -m "🔖 Release version v$NEW_VERSION"
git tag -a "v$NEW_VERSION" -m "Release version v$NEW_VERSION"
git push origin main
git push origin "v$NEW_VERSION"
- name: 📝 Generate Release Notes
if: steps.check-existing.outputs.skip-build == 'false'
id: release-notes
run: |
NEW_VERSION="${{ steps.new-version.outputs.new }}"
CURRENT_VERSION="${{ steps.current-version.outputs.current }}"
# Create comprehensive release notes
cat > release-notes.md << EOF
## 🚀 What's New in v$NEW_VERSION
### 📋 Changes from PR #${{ github.event.pull_request.number }}
**${{ github.event.pull_request.title }}**
${{ needs.check-release-trigger.outputs.release-notes }}
### 📊 Release Information
- **Previous Version:** v$CURRENT_VERSION
- **New Version:** v$NEW_VERSION
- **Version Bump:** ${{ needs.check-release-trigger.outputs.version-bump }}
- **Build Date:** $(date -u +"%Y-%m-%d %H:%M:%S UTC")
- **Commit SHA:** ${{ github.sha }}
### 💻 Installation
1. Download the APK file below
2. Enable "Install from Unknown Sources" in Android settings
3. Install the APK on your device
### 🔧 Technical Details
- **Min SDK:** 21 (Android 5.0)
- **Target SDK:** 34 (Android 14)
- **Architecture:** Universal APK
- **Signed:** Yes ✅
---
*This release was automatically generated by GitHub Actions*
EOF
echo "Release notes generated successfully"
- name: 🎉 Create GitHub Release
if: steps.check-existing.outputs.skip-build == 'false'
uses: softprops/action-gh-release@v1
with:
tag_name: v${{ steps.new-version.outputs.new }}
name: 🚀 ScheduleX v${{ steps.new-version.outputs.new }}
body_path: release-notes.md
files: |
android/app/build/outputs/apk/release/ScheduleX-v${{ steps.new-version.outputs.new }}.apk
draft: false
prerelease: false
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: 🧹 Cleanup
if: always()
run: |
# Remove sensitive files
rm -f android/app/release-key.jks
rm -f android/keystore.properties
echo "🧹 Cleanup completed"
- name: 📱 Post-Release Summary
if: steps.check-existing.outputs.skip-build == 'false'
run: |
NEW_VERSION="${{ steps.new-version.outputs.new }}"
APK_PATH="android/app/build/outputs/apk/release/ScheduleX-v${NEW_VERSION}.apk"
APK_SIZE=$(du -h "$APK_PATH" | cut -f1)
echo "## 🎉 Release Summary"
echo "✅ **Version:** v$NEW_VERSION"
echo "✅ **APK Size:** $APK_SIZE"
echo "✅ **Release URL:** ${{ github.server_url }}/${{ github.repository }}/releases/tag/v$NEW_VERSION"
echo "✅ **Status:** Successfully Released!"
notify-failure:
needs: [check-release-trigger, build-and-release]
if: failure() && needs.check-release-trigger.outputs.should-release == 'true'
runs-on: ubuntu-latest
steps:
- name: 📢 Notify Build Failure
run: |
echo "❌ Release build failed for PR #${{ github.event.pull_request.number }}"
echo "Please check the workflow logs and fix the issues."