Spaces:
Running
Running
File size: 5,894 Bytes
068c3e6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | 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 '''
<!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 ---
@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) |