from flask import Flask, request, send_file, Response import os import zipfile import subprocess import shutil app = Flask(__name__) # --- HELPER: GENERATE KEY (Same as before) --- def generate_keystore(): keystore_path = "debug.keystore" if not os.path.exists(keystore_path): cmd = [ "keytool", "-genkey", "-v", "-keystore", keystore_path, "-storepass", "android", "-alias", "androiddebugkey", "-keypass", "android", "-keyalg", "RSA", "-keysize", "2048", "-validity", "10000", "-dname", "CN=AndroidDebug,O=Android,C=US" ] subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) return keystore_path # --- ROUTE: HOME (HTML FORM FOR TESTING) --- @app.route('/') def index(): return ''' Android Build Server

Upload Project ZIP

''' # --- ROUTE: API ENDPOINT --- @app.route('/upload', methods=['POST']) def upload_file(): if 'file' not in request.files: return "No file part", 400 file = request.files['file'] if file.filename == '': return "No selected file", 400 if file: # 1. Cleanup & Save if os.path.exists("project_extract"): shutil.rmtree("project_extract") os.makedirs("project_extract", exist_ok=True) zip_path = "uploaded_project.zip" file.save(zip_path) # 2. Extract try: with zipfile.ZipFile(zip_path, "r") as zip_ref: zip_ref.extractall("project_extract") except Exception as e: return f"Error extracting ZIP: {str(e)}", 400 # 3. Find Root project_root = None for root, dirs, files in os.walk("project_extract"): if "gradlew" in files: project_root = root break if not project_root: return "Error: ZIP file mein 'gradlew' nahi mila.", 400 # 4. Permissions gradlew_path = os.path.join(project_root, "gradlew") subprocess.run(["chmod", "+x", gradlew_path]) # 5. Build # Hum assembleRelease chala rahe hain build_cmd = ["./gradlew", "assembleRelease", "--no-daemon", "--stacktrace"] try: result = subprocess.run( build_cmd, cwd=project_root, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) if result.returncode == 0: # 6. Find Unsigned APK unsigned_apk = None for root, dirs, files in os.walk(project_root): for f in files: if f.endswith(".apk") and "release" in f: unsigned_apk = os.path.join(root, f) break if unsigned_apk: # 7. SIGNING keystore = generate_keystore() signed_apk_name = "Installable_App.apk" aligned_apk = "aligned.apk" # Full path for output output_apk_path = os.path.abspath(signed_apk_name) # Step A: ZipAlign try: subprocess.run(["zipalign", "-v", "-p", "4", unsigned_apk, aligned_apk], stdout=subprocess.DEVNULL, check=True) apk_to_sign = aligned_apk except: apk_to_sign = unsigned_apk # Step B: APKSigner sign_cmd = [ "apksigner", "sign", "--ks", keystore, "--ks-pass", "pass:android", "--out", output_apk_path, apk_to_sign ] sign_result = subprocess.run(sign_cmd, capture_output=True, text=True) if sign_result.returncode == 0: # CLEANUP ZIP before sending if os.path.exists(zip_path): os.remove(zip_path) # RETURN THE SIGNED APK return send_file( output_apk_path, as_attachment=True, download_name=signed_apk_name, mimetype="application/vnd.android.package-archive" ) else: # Signing Failed - Show Logs logs = sign_result.stderr.splitlines()[:200] return Response("\n".join(logs), status=500, mimetype="text/plain") else: return "Build Success but APK file not found.", 500 else: # BUILD FAILED - RETURN FIRST 200 LINES OF LOGS # Combining stdout and stderr to capture full context full_log = result.stdout + "\n" + result.stderr logs = full_log.splitlines()[:200] error_message = "BUILD FAILED. LOGS (First 200 lines):\n\n" + "\n".join(logs) return Response(error_message, status=500, mimetype="text/plain") except Exception as e: return f"System Error: {str(e)}", 500 if __name__ == '__main__': # Hugging Face Spaces port 7860 use karta hai app.run(host='0.0.0.0', port=7860)