Merge branch 'main' of https://github.com/codebuilderinc/codebuilder-app #59
This file contains 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: π EAS Android Build & Smart Release | |
on: | |
push: | |
branches: [main] | |
workflow_dispatch: | |
env: | |
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
jobs: | |
build-android: | |
name: π¨ Build Android - Version Manager | |
runs-on: ubuntu-latest | |
outputs: | |
build_id: ${{ steps.build.outputs.BUILD_ID }} | |
app_version: ${{ steps.version-control.outputs.app_version }} | |
build_number: ${{ steps.version-control.outputs.build_number }} | |
build_date: ${{ steps.version-control.outputs.build_date }} | |
is_production: ${{ steps.version-control.outputs.is_production }} | |
steps: | |
# ======================== | |
# π οΈ Repository Setup | |
# ======================== | |
- name: "π¦ Checkout (Full History)" | |
uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 | |
# ======================== | |
# βοΈ Environment Configuration | |
# ======================== | |
- name: "π¦ Setup Node.js 20.x" | |
uses: actions/setup-node@v4 | |
with: | |
node-version: 20.x | |
cache: "npm" | |
- name: "𧩠Install dependencies (ci)" | |
run: npm ci | |
# ======================== | |
# π Version Management | |
# ======================== | |
- name: "π Update Production Version" | |
if: github.ref == 'refs/heads/main' | |
run: node scripts/bumpVersion.cjs | |
- name: "π§ Configure Git for Automation" | |
if: github.ref == 'refs/heads/main' | |
run: | | |
git config --global user.name "GitHub Actions" | |
git config --global user.email "[email protected]" | |
- name: "πΎ Commit Version Update" | |
if: github.ref == 'refs/heads/main' | |
run: | | |
git add version.json | |
git commit -m "chore: Auto-increment version [skip ci]" | |
git push | |
# ======================== | |
# π Version Tagging | |
# ======================== | |
- name: "π·οΈ Set CI/CD Versions" | |
id: version-control | |
run: | | |
# Use version from version.json (requires jq) | |
if [ "${{ github.ref }}" == "refs/heads/main" ]; then | |
APP_VERSION=$(jq -r '.version' version.json) | |
IS_PRODUCTION="true" | |
else | |
APP_VERSION="1.0.0-prerelease.${{ github.run_number }}" | |
IS_PRODUCTION="false" | |
fi | |
# Generate build identifiers | |
BUILD_NUMBER="${{ github.run_id }}" | |
BUILD_DATE=$(date +'%Y%m%d-%H%M%S') | |
# Set outputs for downstream jobs | |
echo "app_version=$APP_VERSION" >> $GITHUB_OUTPUT | |
echo "build_number=$BUILD_NUMBER" >> $GITHUB_OUTPUT | |
echo "build_date=$BUILD_DATE" >> $GITHUB_OUTPUT | |
echo "is_production=$IS_PRODUCTION" >> $GITHUB_OUTPUT | |
# Export environment variables | |
echo "APP_VERSION=$APP_VERSION" >> $GITHUB_ENV | |
echo "BUILD_NUMBER=$BUILD_NUMBER" >> $GITHUB_ENV | |
echo "BUILD_DATE=$BUILD_DATE" >> $GITHUB_ENV | |
# ======================== | |
# π EAS Setup & Auth | |
# ======================== | |
- name: "βοΈ Install EAS CLI" | |
run: npm install -g eas-cli@latest | |
- name: "π Verify Expo Credentials" | |
run: npx eas whoami --token $EXPO_TOKEN | |
# ======================== | |
# ποΈ Build Execution | |
# ======================== | |
- name: "π Trigger EAS Build" | |
id: build | |
run: | | |
echo "π Initializing build process..." | |
sudo apt-get install -y jq | |
BUILD_JSON=$(npx eas build -p android --profile production --non-interactive --json) | |
BUILD_ID=$(echo "$BUILD_JSON" | jq -r '.[0].id') | |
echo "BUILD_ID=$BUILD_ID" >> $GITHUB_ENV | |
echo "BUILD_ID=$BUILD_ID" >> $GITHUB_OUTPUT | |
download-apk: | |
name: "π₯ APK Artifact Handler" | |
runs-on: ubuntu-latest | |
needs: build-android | |
outputs: | |
apk_path: ${{ steps.download.outputs.APK_PATH }} | |
steps: | |
# ======================== | |
# π οΈ Environment Setup | |
# ======================== | |
- name: "π¦ Checkout Repository" | |
uses: actions/checkout@v4 | |
- name: "βοΈ Setup Node.js" | |
uses: actions/setup-node@v4 | |
with: | |
node-version: 20.x | |
- name: "π¦ Install Dependencies" | |
run: npm ci | |
# ======================== | |
# π¦ Dependency Management | |
# ======================== | |
- name: "π§° Install Build Tools" | |
run: | | |
npm install -g eas-cli@latest | |
sudo apt-get install -y jq curl dotenv | |
# ======================== | |
# π Build Monitoring | |
# ======================== | |
- name: "β³ Wait for Build Completion" | |
run: | | |
echo "β° Monitoring build status..." | |
cd $GITHUB_WORKSPACE | |
BUILD_ID=${{ needs.build-android.outputs.build_id }} | |
echo "π Starting build monitoring for BUILD_ID: $BUILD_ID" | |
# Initial check without JSON for better error visibility | |
npx eas build:view $BUILD_ID || true | |
RETRY_COUNT=0 | |
MAX_RETRIES=120 | |
SLEEP_TIME=30 | |
while [[ $RETRY_COUNT -lt $MAX_RETRIES ]]; do | |
echo -e "\n=== Attempt $((RETRY_COUNT+1))/$MAX_RETRIES ===" | |
# Fetch build status in JSON format | |
BUILD_STATUS_JSON=$(npx eas build:view --json $BUILD_ID) | |
echo "π Raw API response: $BUILD_STATUS_JSON" | |
# Validate JSON and check for empty response | |
if ! echo "$BUILD_STATUS_JSON" | jq empty >/dev/null 2>&1 || [[ -z "$BUILD_STATUS_JSON" ]]; then | |
echo "β Error: Invalid or empty response from EAS API! Retrying..." | |
RETRY_COUNT=$((RETRY_COUNT+1)) | |
sleep $SLEEP_TIME | |
continue | |
fi | |
BUILD_STATUS=$(echo "$BUILD_STATUS_JSON" | jq -r '.status') | |
ERROR_MESSAGE=$(echo "$BUILD_STATUS_JSON" | jq -r '.error.message // empty') | |
echo "π Parsed status: $BUILD_STATUS" | |
[[ -n "$ERROR_MESSAGE" ]] && echo "β Error message: $ERROR_MESSAGE" | |
case $BUILD_STATUS in | |
"FINISHED") | |
APK_URL=$(echo "$BUILD_STATUS_JSON" | jq -r '.artifacts.buildUrl') | |
if [[ -z "$APK_URL" || "$APK_URL" == "null" ]]; then | |
echo "β Error: Successful build but no APK URL found!" | |
exit 1 | |
fi | |
echo "β APK_URL=$APK_URL" >> $GITHUB_ENV | |
exit 0 | |
;; | |
"ERRORED"|"CANCELLED") | |
echo "β Build failed! Error details:" | |
echo "$BUILD_STATUS_JSON" | jq . | |
exit 1 | |
;; | |
"NEW"|"IN_QUE"|"IN_PROGRESS"|"PENDING") | |
echo "β³ Build is still in progress..." | |
;; | |
*) | |
echo "β Unknown build status: $BUILD_STATUS" | |
exit 1 | |
;; | |
esac | |
RETRY_COUNT=$((RETRY_COUNT+1)) | |
sleep $SLEEP_TIME | |
done | |
echo "β Error: Build did not complete within the expected time!" | |
exit 1 | |
env: | |
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} | |
# ======================== | |
# π¦ Artifact Handling | |
# ======================== | |
- name: "π₯ Download APK" | |
id: download | |
run: | | |
echo "π½ Retrieving APK URL..." | |
APK_URL=$(npx eas build:list --build-id ${{ needs.build-android.outputs.build_id }} --json | jq -r '.[0].artifacts.url') | |
echo "π₯ Downloading APK from $APK_URL..." | |
curl -L "$APK_URL" -o app-release.apk | |
echo "APK_PATH=app-release.apk" >> $GITHUB_OUTPUT | |
- name: "π€ Upload Artifact" | |
uses: actions/upload-artifact@v4 | |
with: | |
name: android-apk | |
path: app-release.apk | |
generate-changelog: | |
name: "π Changelog Generator" | |
runs-on: ubuntu-latest | |
needs: build-android | |
outputs: | |
changelog: ${{ steps.changelog.outputs.CHANGELOG }} | |
steps: | |
# ======================== | |
# π οΈ Repository Setup | |
# ======================== | |
- name: "π Checkout with Full History" | |
uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 | |
# ======================== | |
# π Changelog Generation | |
# ======================== | |
- name: "π Create Release Notes" | |
id: changelog | |
run: | | |
echo "π Generating changelog from git history..." | |
CHANGELOG=$(git log --pretty=format:"- %s (%h) by %an" -n 15) | |
echo "$CHANGELOG" > changelog.txt | |
echo "CHANGELOG=$(cat changelog.txt)" >> $GITHUB_OUTPUT | |
- name: "π€ Upload Changelog" | |
uses: actions/upload-artifact@v4 | |
with: | |
name: changelog | |
path: changelog.txt | |
create-release: | |
name: "π Smart Release Publisher" | |
runs-on: ubuntu-latest | |
needs: [build-android, download-apk, generate-changelog] | |
steps: | |
# ======================== | |
# π₯ Artifact Retrieval | |
# ======================== | |
- name: "π¦ Get APK Artifact" | |
uses: actions/download-artifact@v4 | |
with: | |
name: android-apk | |
- name: "π Get Changelog" | |
uses: actions/download-artifact@v4 | |
with: | |
name: changelog | |
# ======================== | |
# π·οΈ Release Creation | |
# ======================== | |
- name: "ποΈ Determine Release Type" | |
id: release-type | |
run: | | |
echo "π Detecting release type..." | |
if [ "${{ needs.build-android.outputs.is_production }}" = "true" ]; then | |
echo "π’ Production release detected" | |
echo "RELEASE_TAG=v${{ needs.build-android.outputs.app_version }}" >> $GITHUB_ENV | |
echo "RELEASE_TITLE=Production Release v${{ needs.build-android.outputs.app_version }}" >> $GITHUB_ENV | |
else | |
echo "π‘ Nightly build detected" | |
echo "RELEASE_TAG=nightly-${{ needs.build-android.outputs.build_date }}" >> $GITHUB_ENV | |
echo "RELEASE_TITLE=Nightly Build (${{ needs.build-android.outputs.build_date }})" >> $GITHUB_ENV | |
fi | |
- name: "π Publish GitHub Release" | |
uses: softprops/action-gh-release@v2 | |
with: | |
tag_name: ${{ env.RELEASE_TAG }} | |
name: ${{ env.RELEASE_TITLE }} | |
body_path: changelog.txt | |
files: app-release.apk | |
prerelease: ${{ needs.build-android.outputs.is_production != 'true' }} |