Merge branch 'main' of https://github.com/codebuilderinc/codebuilder-app #85
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 | |
# NOTE: Removed cache: "pnpm" to avoid expecting pnpm pre-installed | |
- name: "π§ Install pnpm" | |
run: | | |
npm install -g pnpm | |
pnpm --version | |
- name: "𧩠Install dependencies (ci)" | |
run: pnpm install --frozen-lockfile | |
# ======================== | |
# π Version Management | |
# ======================== | |
- name: "π Update Production Version" | |
if: github.ref == 'refs/heads/main' | |
run: node scripts/bumpVersion.js | |
- 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: "π§ Setup pnpm global bin" | |
env: | |
SHELL: /bin/bash | |
run: | | |
mkdir -p $HOME/.pnpm-global | |
echo "PNPM_HOME=$HOME/.pnpm-global" >> $GITHUB_ENV | |
echo "$HOME/.pnpm-global" >> $GITHUB_PATH | |
pnpm setup | |
- name: "βοΈ Install EAS CLI" | |
run: pnpm add -g eas-cli@latest | |
- name: "π Verify Expo Credentials" | |
run: eas whoami --token $EXPO_TOKEN | |
env: | |
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} | |
# ======================== | |
# Ensure consistent pnpm store: Force reinstallation of dependencies | |
# ======================== | |
- name: "Set pnpm global store directory" | |
run: pnpm config set store-dir "$HOME/.local/share/pnpm/store" --global | |
- name: "Reinstall dependencies with new store" | |
run: pnpm install --force | |
- name: "Install @expo/config-plugins" | |
run: pnpm add @expo/config-plugins | |
# ======================== | |
# Clear npx cache to avoid stale module resolution issues | |
# ======================== | |
- name: "Clear npx cache" | |
run: npx clear-npx-cache | |
# ======================== | |
# ποΈ Build Execution | |
# ======================== | |
- name: "π Trigger EAS Build" | |
id: build | |
run: | | |
echo "π Initializing build process..." | |
sudo apt-get install -y jq | |
BUILD_JSON=$(eas build -p android --profile production --non-interactive --json) | |
echo "Raw build output: $BUILD_JSON" | |
BUILD_ID=$(echo "$BUILD_JSON" | jq -r '.[0].id') | |
if [[ -z "$BUILD_ID" || "$BUILD_ID" == "null" ]]; then | |
echo "Error: Failed to retrieve BUILD_ID!" | |
exit 1 | |
fi | |
echo "EAS Build started with ID: $BUILD_ID" | |
echo "BUILD_ID=$BUILD_ID" >> $GITHUB_ENV | |
echo "BUILD_ID=$BUILD_ID" >> $GITHUB_OUTPUT | |
env: | |
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} | |
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 | |
# NOTE: Removed cache setting here as well | |
- name: "π§ Install pnpm" | |
run: | | |
npm install -g pnpm | |
pnpm --version | |
- name: "π¦ Install Dependencies" | |
run: pnpm install --frozen-lockfile | |
# ======================== | |
# π¦ Dependency Management | |
# ======================== | |
- name: "π§° Install Build Tools" | |
run: | | |
pnpm add -g eas-cli@latest | |
sudo apt-get update | |
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 | |
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=$(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=$(eas build:view --json ${{ needs.build-android.outputs.build_id }} | jq -r '.artifacts.buildUrl') | |
if [[ -z "$APK_URL" || "$APK_URL" == "null" ]]; then | |
echo "β Error: No APK URL found!" | |
exit 1 | |
fi | |
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 | |
# FIXED OUTPUT HANDLING (ONLY CHANGE) | |
delimiter=$(openssl rand -hex 6) | |
echo "CHANGELOG<<${delimiter}" >> $GITHUB_OUTPUT | |
cat changelog.txt >> $GITHUB_OUTPUT | |
echo "${delimiter}" >> $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" | |
RELEASE_TAG="v${{ needs.build-android.outputs.app_version }}" | |
RELEASE_TITLE="Production Release v${{ needs.build-android.outputs.app_version }}" | |
else | |
echo "π‘ Nightly build detected" | |
RELEASE_TAG="nightly-${{ needs.build-android.outputs.build_date }}" | |
RELEASE_TITLE="Nightly Build (${{ needs.build-android.outputs.build_date }})" | |
fi | |
echo "RELEASE_TAG=${RELEASE_TAG}" >> $GITHUB_OUTPUT | |
- name: "π Publish GitHub Release" | |
uses: softprops/action-gh-release@v2 | |
with: | |
tag_name: ${{ steps.release-type.outputs.RELEASE_TAG }} | |
name: ${{ steps.release-type.outputs.RELEASE_TITLE }} | |
body_path: changelog.txt | |
files: app-release.apk | |
prerelease: ${{ needs.build-android.outputs.is_production != 'true' }} |