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)