name: đŸ“Ļ Product Release on: workflow_dispatch: inputs: app: description: 'đŸ“Ļ Product' required: true type: choice options: - worker-mobile-app - client-mobile-app environment: description: '🌍 Environment' required: true type: choice options: - dev - stage - prod create_github_release: description: 'đŸ“Ļ Create GitHub Release' required: true type: boolean default: true prerelease: description: '🔖 Mark as Pre-release' required: false type: boolean default: false jobs: validate-and-create-release: name: 🚀 Create Product Release runs-on: ubuntu-latest permissions: contents: write outputs: version: ${{ steps.version.outputs.version }} tag_name: ${{ steps.tag.outputs.tag_name }} steps: - name: đŸ“Ĩ Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 - name: đŸƒđŸžâ€â™‚ī¸ Make scripts executable run: | chmod +x .github/scripts/*.sh echo "✅ Scripts are now executable" - name: 📖 Extract version from version file id: version run: | VERSION=$(.github/scripts/extract-version.sh "${{ github.event.inputs.app }}") echo "version=${VERSION}" >> $GITHUB_OUTPUT echo "📌 Extracted version: ${VERSION}" - name: đŸˇī¸ Generate tag name id: tag run: | TAG_NAME=$(.github/scripts/generate-tag-name.sh \ "${{ github.event.inputs.app }}" \ "${{ github.event.inputs.environment }}" \ "${{ steps.version.outputs.version }}") echo "tag_name=${TAG_NAME}" >> $GITHUB_OUTPUT echo "đŸŽ¯ Tag to create: ${TAG_NAME}" - name: 🔍 Check if tag already exists run: | TAG_NAME="${{ steps.tag.outputs.tag_name }}" if git rev-parse "$TAG_NAME" >/dev/null 2>&1; then echo "❌ Error: Tag $TAG_NAME already exists" echo "💡 Tip: Update the version in the version file before creating a new release" exit 1 fi echo "✅ Tag does not exist, proceeding..." - name: 📋 Extract release notes from CHANGELOG id: release_notes run: | .github/scripts/extract-release-notes.sh \ "${{ github.event.inputs.app }}" \ "${{ steps.version.outputs.version }}" \ "${{ github.event.inputs.environment }}" \ "${{ steps.tag.outputs.tag_name }}" \ "/tmp/release_notes.md" echo "notes_file=/tmp/release_notes.md" >> $GITHUB_OUTPUT - name: đŸˇī¸ Create Git Tag run: | TAG_NAME="${{ steps.tag.outputs.tag_name }}" APP="${{ github.event.inputs.app }}" ENV="${{ github.event.inputs.environment }}" VERSION="${{ steps.version.outputs.version }}" git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git tag -a "$TAG_NAME" -m "🚀 Release ${APP} product ${VERSION} to ${ENV}" git push origin "$TAG_NAME" echo "✅ Tag created and pushed: $TAG_NAME" - name: đŸ“Ļ Create GitHub Release if: ${{ github.event.inputs.create_github_release == 'true' }} env: GH_TOKEN: ${{ github.token }} run: | TAG_NAME="${{ steps.tag.outputs.tag_name }}" APP="${{ github.event.inputs.app }}" ENV="${{ github.event.inputs.environment }}" VERSION="${{ steps.version.outputs.version }}" # Generate release title if [ "$APP" = "worker-mobile-app" ]; then APP_DISPLAY="Worker Mobile Application" else APP_DISPLAY="Client Mobile Application" fi ENV_UPPER=$(echo "$ENV" | tr '[:lower:]' '[:upper:]') RELEASE_NAME="Krow With Us - ${APP_DISPLAY} - ${ENV_UPPER} - v${VERSION}" echo "đŸ“Ļ Creating GitHub Release: $RELEASE_NAME" # Create release if [ "${{ github.event.inputs.prerelease }}" = "true" ]; then gh release create "$TAG_NAME" \ --title "$RELEASE_NAME" \ --notes-file "${{ steps.release_notes.outputs.notes_file }}" \ --prerelease echo "🔖 Pre-release created successfully" else gh release create "$TAG_NAME" \ --title "$RELEASE_NAME" \ --notes-file "${{ steps.release_notes.outputs.notes_file }}" echo "✅ Release created successfully" fi - name: 📊 Generate Release Summary run: | .github/scripts/create-release-summary.sh \ "${{ github.event.inputs.app }}" \ "${{ github.event.inputs.environment }}" \ "${{ steps.version.outputs.version }}" \ "${{ steps.tag.outputs.tag_name }}" build-mobile-artifacts: name: 📱 Build Mobile APK runs-on: ubuntu-latest needs: validate-and-create-release if: ${{ github.event.inputs.app == 'worker-mobile-app' || github.event.inputs.app == 'client-mobile-app' }} permissions: contents: write steps: - name: đŸ“Ĩ Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 - name: đŸŸĸ Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' - name: đŸ”Ĩ Install Firebase CLI run: | npm install -g firebase-tools firebase --version echo "â„šī¸ Note: Firebase CLI installed for Data Connect SDK generation" echo "â„šī¸ If SDK generation fails, ensure Data Connect SDK files are committed to repo" - name: ☕ Setup Java uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: '17' - name: đŸĻ Setup Flutter uses: subosito/flutter-action@v2 with: flutter-version: '3.24.5' channel: 'stable' cache: true - name: 🔧 Install Melos run: | dart pub global activate melos echo "$HOME/.pub-cache/bin" >> $GITHUB_PATH - name: đŸ“Ļ Install Dependencies run: | make mobile-install - name: 🔐 Setup APK Signing env: # Worker Mobile (Staff App) Secrets WORKER_KEYSTORE_DEV_BASE64: ${{ secrets.WORKER_KEYSTORE_DEV_BASE64 }} WORKER_KEYSTORE_STAGING_BASE64: ${{ secrets.WORKER_KEYSTORE_STAGING_BASE64 }} WORKER_KEYSTORE_PROD_BASE64: ${{ secrets.WORKER_KEYSTORE_PROD_BASE64 }} WORKER_KEYSTORE_PASSWORD_DEV: ${{ secrets.WORKER_KEYSTORE_PASSWORD_DEV }} WORKER_KEYSTORE_PASSWORD_STAGING: ${{ secrets.WORKER_KEYSTORE_PASSWORD_STAGING }} WORKER_KEYSTORE_PASSWORD_PROD: ${{ secrets.WORKER_KEYSTORE_PASSWORD_PROD }} WORKER_KEY_ALIAS_DEV: ${{ secrets.WORKER_KEY_ALIAS_DEV }} WORKER_KEY_ALIAS_STAGING: ${{ secrets.WORKER_KEY_ALIAS_STAGING }} WORKER_KEY_ALIAS_PROD: ${{ secrets.WORKER_KEY_ALIAS_PROD }} WORKER_KEY_PASSWORD_DEV: ${{ secrets.WORKER_KEY_PASSWORD_DEV }} WORKER_KEY_PASSWORD_STAGING: ${{ secrets.WORKER_KEY_PASSWORD_STAGING }} WORKER_KEY_PASSWORD_PROD: ${{ secrets.WORKER_KEY_PASSWORD_PROD }} # Client Mobile Secrets CLIENT_KEYSTORE_DEV_BASE64: ${{ secrets.CLIENT_KEYSTORE_DEV_BASE64 }} CLIENT_KEYSTORE_STAGING_BASE64: ${{ secrets.CLIENT_KEYSTORE_STAGING_BASE64 }} CLIENT_KEYSTORE_PROD_BASE64: ${{ secrets.CLIENT_KEYSTORE_PROD_BASE64 }} CLIENT_KEYSTORE_PASSWORD_DEV: ${{ secrets.CLIENT_KEYSTORE_PASSWORD_DEV }} CLIENT_KEYSTORE_PASSWORD_STAGING: ${{ secrets.CLIENT_KEYSTORE_PASSWORD_STAGING }} CLIENT_KEYSTORE_PASSWORD_PROD: ${{ secrets.CLIENT_KEYSTORE_PASSWORD_PROD }} CLIENT_KEY_ALIAS_DEV: ${{ secrets.CLIENT_KEY_ALIAS_DEV }} CLIENT_KEY_ALIAS_STAGING: ${{ secrets.CLIENT_KEY_ALIAS_STAGING }} CLIENT_KEY_ALIAS_PROD: ${{ secrets.CLIENT_KEY_ALIAS_PROD }} CLIENT_KEY_PASSWORD_DEV: ${{ secrets.CLIENT_KEY_PASSWORD_DEV }} CLIENT_KEY_PASSWORD_STAGING: ${{ secrets.CLIENT_KEY_PASSWORD_STAGING }} CLIENT_KEY_PASSWORD_PROD: ${{ secrets.CLIENT_KEY_PASSWORD_PROD }} run: | .github/scripts/setup-apk-signing.sh \ "${{ github.event.inputs.app }}" \ "${{ github.event.inputs.environment }}" \ "${{ runner.temp }}" - name: đŸ—ī¸ Build APK id: build_apk run: | APP="${{ github.event.inputs.app }}" if [ "$APP" = "worker-mobile-app" ]; then echo "📱 Building Staff (Worker) APK..." make mobile-staff-build PLATFORM=apk MODE=release APP_NAME="staff" else echo "📱 Building Client APK..." make mobile-client-build PLATFORM=apk MODE=release APP_NAME="client" fi # Find the generated APK (Flutter places it in build/app/outputs/flutter-apk/) APK_PATH=$(find apps/mobile/apps/${APP_NAME}/build/app/outputs/flutter-apk -name "app-release.apk" 2>/dev/null | head -n 1) # Fallback to searching entire apps directory if not found if [ -z "$APK_PATH" ]; then APK_PATH=$(find apps/mobile/apps/${APP_NAME} -name "app-release.apk" | head -n 1) fi if [ -z "$APK_PATH" ]; then echo "❌ Error: APK not found!" echo "Searched in apps/mobile/apps/${APP_NAME}/" find apps/mobile/apps/${APP_NAME} -name "*.apk" || echo "No APK files found" exit 1 fi echo "✅ APK built successfully: $APK_PATH" echo "app_name=${APP_NAME}" >> $GITHUB_OUTPUT echo "apk_path=${APK_PATH}" >> $GITHUB_OUTPUT - name: ✅ Verify APK Signature run: | .github/scripts/verify-apk-signature.sh "${{ steps.build_apk.outputs.apk_path }}" - name: 📤 Upload APK as Artifact uses: actions/upload-artifact@v4 with: name: ${{ github.event.inputs.app }}-${{ needs.validate-and-create-release.outputs.version }}-${{ github.event.inputs.environment }} path: apps/mobile/apps/${{ steps.build_apk.outputs.app_name }}/build/app/outputs/flutter-apk/app-release.apk if-no-files-found: error retention-days: 30 - name: đŸ“Ļ Attach APK to GitHub Release if: ${{ github.event.inputs.create_github_release == 'true' }} env: GH_TOKEN: ${{ github.token }} run: | .github/scripts/attach-apk-to-release.sh \ "${{ needs.validate-and-create-release.outputs.tag_name }}" \ "${{ github.event.inputs.app }}" \ "${{ steps.build_apk.outputs.app_name }}" \ "${{ needs.validate-and-create-release.outputs.version }}" \ "${{ github.event.inputs.environment }}"