-
-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathrelease.py
More file actions
182 lines (164 loc) · 6.7 KB
/
Copy pathrelease.py
File metadata and controls
182 lines (164 loc) · 6.7 KB
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
import json
import subprocess
import tarfile
import os
import tempfile
import sys
from datetime import datetime, UTC
def read_properties_file(path):
properties = {}
with open(path, "r") as file:
for line in file:
stripped = line.strip()
if not stripped or stripped.startswith("#"):
continue
key, sep, value = line.partition("=")
if not sep:
continue
properties[key.strip()] = value.strip()
return properties
def get_manifest(appPath):
"""Extract only the file named 'manifest.properties' from the given tar/tar.gz
archive into the directory specified by outputPath.
Returns True on success, False on failure.
"""
print(f"Unpacking manifest.properties from {appPath}")
if not os.path.isfile(appPath):
print(f"File not found: {appPath}")
return False
try:
# Auto-detect compression (tar, tar.gz, tar.bz2, etc.)
with tarfile.open(appPath, mode="r:*") as tf:
# Find a member whose base name is exactly 'manifest.properties'
member = None
for m in tf.getmembers():
# Skip directories and non-files
if not m.isfile():
continue
if os.path.basename(m.name) == "manifest.properties":
member = m
break
if member is None:
print("manifest.properties not found in archive")
return None
# Read the member's content and write it directly into outputPath
# Create a temporary directory that persists after this function returns.
temp_dir = tempfile.mkdtemp(prefix="tactility_app_")
dest_file = os.path.join(temp_dir, "manifest.properties")
with tf.extractfile(member) as src, open(dest_file, "wb") as dst:
if src is None:
print("Failed to open manifest.properties from the archive")
return False
while True:
chunk = src.read(1024 * 1024)
if not chunk:
break
dst.write(chunk)
properties = read_properties_file(dest_file)
os.remove(dest_file)
os.rmdir(temp_dir)
return properties
except tarfile.ReadError as e:
print(f"Not a valid tar archive: {appPath} ({e})")
except Exception as e:
print(f"Failed to extract manifest.properties from {appPath}: {e}")
return None
def get_versioned_file_name(manifest):
app_id = manifest["app.id"]
version_code = manifest["app.version.code"]
return f"{app_id}-{version_code}.app"
def get_os_version(manifest):
sdk = manifest["target.sdk"]
# Remove trailing hyphen suffix if present
if "-" in sdk:
return sdk.rsplit("-", 1)[0].strip()
else:
return sdk
def check_and_get_sdk_version(manifest_map):
"""Ensure all apps target the same (simplified) SDK version and return it."""
versions = {get_os_version(manifest) for manifest in manifest_map.values()}
if len(versions) != 1:
print(f"ERROR: Apps target multiple SDK versions: {sorted(versions)}. All apps must target the same SDK version.")
sys.exit(1)
return next(iter(versions))
def get_git_commit_hash():
return subprocess.check_output(['git', 'rev-parse', 'HEAD']).decode('ascii').strip()
def manifest_config_to_flat_json(manifest):
"""Convert a flat (V2) manifest dict into a flat JSON-like dict.
Expected keys:
app.id -> appId
app.version.name -> appVersionName
app.version.code -> appVersionCode (int)
app.name -> appName
app.description -> appDescription (optional; default "")
target.sdk -> targetSdk
target.platforms -> targetPlatforms (comma-separated list)
Unknown/missing values fall back to sensible defaults per requirements.
"""
# Map values
app_id = manifest.get("app.id", "")
app_version_name = manifest.get("app.version.name", "")
app_version_code_raw = manifest.get("app.version.code", "0")
app_name = manifest.get("app.name", "")
app_description = manifest.get("app.description", "") or ""
# Coerce version code to int safely
try:
app_version_code = int(str(app_version_code_raw).strip())
except Exception:
app_version_code = 0
target_sdk = manifest.get("target.sdk", "")
platforms_raw = manifest.get("target.platforms", "")
target_platforms = [p.strip() for p in str(platforms_raw).split(",") if p.strip()] if platforms_raw is not None else []
filename = get_versioned_file_name(manifest)
return {
"appId": app_id,
"appVersionName": app_version_name,
"appVersionCode": app_version_code,
"appName": app_name,
"appDescription": app_description,
"targetSdk": target_sdk,
"targetPlatforms": target_platforms,
"file": filename
}
def write_json(filepath, data):
text = json.dumps(data)
with open(filepath, 'w') as f:
f.write(text)
if __name__ == "__main__":
print("Tactility app release script")
if len(sys.argv) != 2:
print("Missing argument")
sys.exit()
app_directory = sys.argv[1]
manifest_map = {}
any_manifest = None
if os.path.exists(app_directory):
for file in os.listdir(app_directory):
if file.endswith(".app"):
file_path = os.path.join(app_directory, file)
manifest_map[file_path] = get_manifest(file_path)
# All bundled apps must target the same SDK version; this becomes the CDN path segment
sdk_version = check_and_get_sdk_version(manifest_map)
with open("sdk_version.txt", "w") as f:
f.write(sdk_version)
print(f"SDK version: {sdk_version}")
output_json = {
"sdkVersion": sdk_version,
"created": datetime.now(UTC).strftime('%Y-%m-%dT%H:%M:%SZ'),
"gitCommit": get_git_commit_hash(),
"apps": []
}
# Rename files and collect manifest data into output json object
for file_path in manifest_map.keys():
print(f"Processing {file_path}: {manifest_map[file_path]}")
manifest = manifest_map[file_path]
manifest_json = manifest_config_to_flat_json(manifest)
output_json["apps"].append(manifest_json)
release_file_name = get_versioned_file_name(manifest)
renamed_file_path = os.path.join(app_directory, release_file_name)
os.rename(file_path, renamed_file_path)
print(f"Renamed {file_path} to {renamed_file_path}")
any_manifest = manifest
# Write JSON
output_json_path = os.path.join(app_directory, "apps.json")
write_json(output_json_path, output_json)