forked from espanso/espanso
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpackager.py
254 lines (202 loc) · 9.21 KB
/
packager.py
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
import subprocess
import sys
import os
import platform
import hashlib
import click
import shutil
import toml
import hashlib
import glob
import urllib.request
from dataclasses import dataclass
PACKAGER_TARGET_DIR = "target/packager"
@dataclass
class PackageInfo:
name: str
version: str
description: str
publisher: str
url: str
modulo_version: str
@click.group()
def cli():
pass
@cli.command()
@click.option('--skipcargo', default=False, is_flag=True, help="Skip cargo release build")
def build(skipcargo):
"""Build espanso distribution"""
# Check operating system
TARGET_OS = "macos"
if platform.system() == "Windows":
TARGET_OS = "windows"
elif platform.system() == "Linux":
TARGET_OS = "linux"
print("Detected OS:", TARGET_OS)
print("Loading info from Cargo.toml")
cargo_info = toml.load("Cargo.toml")
package_info = PackageInfo(cargo_info["package"]["name"],
cargo_info["package"]["version"],
cargo_info["package"]["description"],
cargo_info["package"]["authors"][0],
cargo_info["package"]["homepage"],
cargo_info["modulo"]["version"])
print(package_info)
if not skipcargo:
print("Building release version...")
subprocess.run(["cargo", "build", "--release"])
else:
print("Skipping build")
if TARGET_OS == "windows":
build_windows(package_info)
elif TARGET_OS == "macos":
build_mac(package_info)
def calculate_sha256(file):
with open(file, "rb") as f:
b = f.read() # read entire file as bytes
readable_hash = hashlib.sha256(b).hexdigest()
return readable_hash
def build_windows(package_info):
print("Starting packaging process for Windows...")
# Check Inno Setup
try:
subprocess.run(["iscc"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
except FileNotFoundError:
raise Exception("Could not find Inno Setup compiler. Please install it from here: http://www.jrsoftware.org/isdl.php")
print("Clearing target dirs")
# Clearing previous build directory
if os.path.isdir(PACKAGER_TARGET_DIR):
print("Cleaning packager temp directory...")
shutil.rmtree(PACKAGER_TARGET_DIR)
TARGET_DIR = os.path.join(PACKAGER_TARGET_DIR, "win")
os.makedirs(TARGET_DIR, exist_ok=True)
modulo_url = "https://github.com/federico-terzi/modulo/releases/download/v{0}/modulo-win.exe".format(package_info.modulo_version)
modulo_sha_url = "https://github.com/federico-terzi/modulo/releases/download/v{0}/modulo-win.exe.sha256.txt".format(package_info.modulo_version)
print("Pulling modulo depencency from:", modulo_url)
modulo_target_file = os.path.join(TARGET_DIR, "modulo.exe")
urllib.request.urlretrieve(modulo_url, modulo_target_file)
print("Pulling SHA signature from:", modulo_sha_url)
modulo_sha_file = os.path.join(TARGET_DIR, "modulo.sha256")
urllib.request.urlretrieve(modulo_sha_url, modulo_sha_file)
print("Checking signatures...")
expected_sha = None
with open(modulo_sha_file, "r") as sha_f:
expected_sha = sha_f.read()
actual_sha = calculate_sha256(modulo_target_file)
if actual_sha != expected_sha:
raise Exception("Modulo SHA256 is not matching")
print("Gathering CRT DLLs...")
msvc_dirs = glob.glob("C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\*\\VC\\Redist\\MSVC\\*")
print("Found Redists: ", msvc_dirs)
print("Determining best redist...")
if len(msvc_dirs) == 0:
raise Exception("Cannot find redistributable dlls")
msvc_dir = None
for curr_dir in msvc_dirs:
dll_files = glob.glob(curr_dir + "\\x64\\*CRT\\*.dll")
print("Found dlls", dll_files, "in", curr_dir)
if any("vcruntime140_1.dll" in x.lower() for x in dll_files):
msvc_dir = curr_dir
break
if msvc_dir is None:
raise Exception("Cannot find redist with VCRUNTIME140_1.dll")
print("Using: ", msvc_dir)
dll_files = glob.glob(msvc_dir + "\\x64\\*CRT\\*.dll")
print("Found DLLs:")
include_list = []
for dll in dll_files:
print("Including: "+dll)
include_list.append("Source: \""+dll+"\"; DestDir: \"{app}\"; Flags: ignoreversion")
print("Including modulo")
include_list.append("Source: \""+os.path.abspath(modulo_target_file)+"\"; DestDir: \"{app}\"; Flags: ignoreversion")
include = "\r\n".join(include_list)
INSTALLER_NAME = f"espanso-win-installer"
# Inno setup
shutil.copy("packager/win/modpath.iss", os.path.join(TARGET_DIR, "modpath.iss"))
print("Processing inno setup template")
with open("packager/win/setupscript.iss", "r") as iss_script:
content = iss_script.read()
# Replace variables
content = content.replace("{{{app_name}}}", package_info.name)
content = content.replace("{{{app_version}}}", package_info.version)
content = content.replace("{{{app_publisher}}}", package_info.publisher)
content = content.replace("{{{app_url}}}", package_info.url)
content = content.replace("{{{app_license}}}", os.path.abspath("LICENSE"))
content = content.replace("{{{app_icon}}}", os.path.abspath("packager/win/icon.ico"))
content = content.replace("{{{executable_path}}}", os.path.abspath("target/release/espanso.exe"))
content = content.replace("{{{output_dir}}}", os.path.abspath(TARGET_DIR))
content = content.replace("{{{output_name}}}", INSTALLER_NAME)
content = content.replace("{{{dll_include}}}", include)
with open(os.path.join(TARGET_DIR, "setupscript.iss"), "w") as output_script:
output_script.write(content)
print("Compiling installer with Inno setup")
subprocess.run(["iscc", os.path.abspath(os.path.join(TARGET_DIR, "setupscript.iss"))])
print("Calculating the SHA256")
sha256_hash = hashlib.sha256()
with open(os.path.abspath(os.path.join(TARGET_DIR, INSTALLER_NAME+".exe")),"rb") as f:
# Read and update hash string value in blocks of 4K
for byte_block in iter(lambda: f.read(4096),b""):
sha256_hash.update(byte_block)
hash_file = os.path.abspath(os.path.join(TARGET_DIR, "espanso-win-installer-sha256.txt"))
with open(hash_file, "w") as hf:
hf.write(sha256_hash.hexdigest())
def build_mac(package_info):
print("Starting packaging process for MacOS...")
print("Clearing target dirs")
# Clearing previous build directory
if os.path.isdir(PACKAGER_TARGET_DIR):
print("Cleaning packager temp directory...")
shutil.rmtree(PACKAGER_TARGET_DIR)
TARGET_DIR = os.path.join(PACKAGER_TARGET_DIR, "mac")
os.makedirs(TARGET_DIR, exist_ok=True)
print("Compressing release to archive...")
target_name = f"espanso-mac.tar.gz"
archive_target = os.path.abspath(os.path.join(TARGET_DIR, target_name))
subprocess.run(["tar",
"-C", os.path.abspath("target/release"),
"-cvf",
archive_target,
"espanso",
])
print(f"Created archive: {archive_target}")
print("Calculating the SHA256")
sha256_hash = hashlib.sha256()
with open(archive_target,"rb") as f:
# Read and update hash string value in blocks of 4K
for byte_block in iter(lambda: f.read(4096),b""):
sha256_hash.update(byte_block)
hash_file = os.path.abspath(os.path.join(TARGET_DIR, "espanso-mac-sha256.txt"))
with open(hash_file, "w") as hf:
hf.write(sha256_hash.hexdigest())
modulo_sha_url = "https://github.com/federico-terzi/modulo/releases/download/v{0}/modulo-mac.sha256.txt".format(package_info.modulo_version)
print("Pulling SHA signature from:", modulo_sha_url)
modulo_sha_file = os.path.join(TARGET_DIR, "modulo.sha256")
urllib.request.urlretrieve(modulo_sha_url, modulo_sha_file)
modulo_sha = None
with open(modulo_sha_file, "r") as sha_f:
modulo_sha = sha_f.read()
if modulo_sha is None:
raise Exception("Cannot determine modulo SHA")
print("Processing Homebrew formula template")
with open("packager/mac/espanso.rb", "r") as formula_template:
content = formula_template.read()
# Replace variables
content = content.replace("{{{app_desc}}}", package_info.description)
content = content.replace("{{{app_url}}}", package_info.url)
content = content.replace("{{{app_version}}}", package_info.version)
content = content.replace("{{{modulo_version}}}", package_info.modulo_version)
content = content.replace("{{{modulo_sha}}}", modulo_sha)
# Calculate hash
with open(archive_target, "rb") as f:
bytes = f.read()
readable_hash = hashlib.sha256(bytes).hexdigest()
content = content.replace("{{{release_hash}}}", readable_hash)
with open(os.path.join(TARGET_DIR, "espanso.rb"), "w") as output_script:
output_script.write(content)
print("Done!")
if __name__ == '__main__':
print("[[ espanso packager ]]")
# Check python version 3
if sys.version_info[0] < 3:
raise Exception("Must be using Python 3")
cli()