Spaces:
Sleeping
Sleeping
| 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) --- | |
| def index(): | |
| return ''' | |
| <!doctype html> | |
| <title>Android Build Server</title> | |
| <h1>Upload Project ZIP</h1> | |
| <form action="/upload" method="post" enctype="multipart/form-data"> | |
| <input type="file" name="file" required> | |
| <input type="submit" value="Build & Sign APK"> | |
| </form> | |
| ''' | |
| # --- ROUTE: API ENDPOINT --- | |
| 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) |