1#! /usr/bin/env nix-shell
2#! nix-shell -i python -p python3.pkgs.click python3.pkgs.click-log nix
3"""
4electron updater
5
6A script for updating binary hashes.
7
8It supports the following modes:
9
10| Mode | Description |
11|------------- | ----------------------------------------------- |
12| `update` | for updating a specific Electron release |
13| `update-all` | for updating all electron releases at once |
14
15The `update` command requires a `--version` flag
16to specify the major release to be update.
17The `update-all` command updates all non-eol major releases.
18
19The `update` and `update-all` commands accept an optional `--commit`
20flag to automatically commit the changes for you.
21"""
22import logging
23import os
24import subprocess
25import sys
26import click
27import click_log
28
29from typing import Tuple
30os.chdir(os.path.dirname(__file__))
31sys.path.append("..")
32from update_util import *
33
34
35# Relatice path to the electron-bin info.json
36BINARY_INFO_JSON = "info.json"
37
38# Relative path the the electron-chromedriver info.json
39CHROMEDRIVER_INFO_JSON = "../chromedriver/info.json"
40
41logger = logging.getLogger(__name__)
42click_log.basic_config(logger)
43
44
45systems = {
46 "i686-linux": "linux-ia32",
47 "x86_64-linux": "linux-x64",
48 "armv7l-linux": "linux-armv7l",
49 "aarch64-linux": "linux-arm64",
50 "x86_64-darwin": "darwin-x64",
51 "aarch64-darwin": "darwin-arm64",
52}
53
54def get_shasums256(version: str) -> list:
55 """Returns the contents of SHASUMS256.txt"""
56 try:
57 called_process: subprocess.CompletedProcess = subprocess.run(
58 [
59 "nix-prefetch-url",
60 "--print-path",
61 f"https://github.com/electron/electron/releases/download/v{version}/SHASUMS256.txt",
62 ],
63 capture_output=True,
64 check=True,
65 text=True,
66 )
67
68 hash_file_path = called_process.stdout.split("\n")[1]
69
70 with open(hash_file_path, "r") as f:
71 return f.read().split("\n")
72
73 except subprocess.CalledProcessError as err:
74 print(err.stderr)
75 sys.exit(1)
76
77
78def get_headers(version: str) -> str:
79 """Returns the hash of the release headers tarball"""
80 try:
81 called_process: subprocess.CompletedProcess = subprocess.run(
82 [
83 "nix-prefetch-url",
84 "--unpack",
85 f"https://artifacts.electronjs.org/headers/dist/v{version}/node-v{version}-headers.tar.gz",
86 ],
87 capture_output=True,
88 check=True,
89 text=True,
90 )
91 return called_process.stdout.split("\n")[0]
92 except subprocess.CalledProcessError as err:
93 print(err.stderr)
94 sys.exit(1)
95
96
97def get_electron_hashes(major_version: str) -> dict:
98 """Returns a dictionary of hashes for a given major version"""
99 m, _ = get_latest_version(major_version)
100 version: str = m["version"]
101
102 out = {}
103 out[major_version] = {
104 "hashes": {},
105 "version": version,
106 }
107
108 hashes: list = get_shasums256(version)
109
110 for nix_system, electron_system in systems.items():
111 filename = f"*electron-v{version}-{electron_system}.zip"
112 if any([x.endswith(filename) for x in hashes]):
113 out[major_version]["hashes"][nix_system] = [
114 x.split(" ")[0] for x in hashes if x.endswith(filename)
115 ][0]
116 out[major_version]["hashes"]["headers"] = get_headers(version)
117
118 return out
119
120
121def get_chromedriver_hashes(major_version: str) -> dict:
122 """Returns a dictionary of hashes for a given major version"""
123 m, _ = get_latest_version(major_version)
124 version: str = m["version"]
125
126 out = {}
127 out[major_version] = {
128 "hashes": {},
129 "version": version,
130 }
131
132 hashes: list = get_shasums256(version)
133
134 for nix_system, electron_system in systems.items():
135 filename = f"*chromedriver-v{version}-{electron_system}.zip"
136 if any([x.endswith(filename) for x in hashes]):
137 out[major_version]["hashes"][nix_system] = [
138 x.split(" ")[0] for x in hashes if x.endswith(filename)
139 ][0]
140 out[major_version]["hashes"]["headers"] = get_headers(version)
141
142 return out
143
144
145def update_binary(major_version: str, commit: bool, chromedriver: bool) -> None:
146 """Update a given electron-bin or chromedriver release
147
148 Args:
149 major_version: The major version number, e.g. '27'
150 commit: Whether the updater should commit the result
151 """
152 if chromedriver:
153 json_path=CHROMEDRIVER_INFO_JSON
154 package_name = f"electron-chromedriver_{major_version}"
155 update_fn=get_chromedriver_hashes
156 else:
157 json_path=BINARY_INFO_JSON
158 package_name = f"electron_{major_version}-bin"
159 update_fn = get_electron_hashes
160 print(f"Updating {package_name}")
161
162 old_info = load_info_json(json_path)
163 new_info = update_fn(major_version)
164
165 out = old_info | new_info
166
167 save_info_json(json_path, out)
168
169 old_version = (
170 old_info[major_version]["version"] if major_version in old_info else None
171 )
172 new_version = new_info[major_version]["version"]
173 if old_version == new_version:
174 print(f"{package_name} is up-to-date")
175 elif commit:
176 commit_result(package_name, old_version, new_version, json_path)
177
178
179@click.group()
180def cli() -> None:
181 """A script for updating electron-bin and chromedriver hashes"""
182 pass
183
184
185@cli.command("update-chromedriver", help="Update a single major release")
186@click.option("-v", "--version", help="The major version, e.g. '23'")
187@click.option("-c", "--commit", is_flag=True, default=False, help="Commit the result")
188def update_chromedriver(version: str, commit: bool) -> None:
189 update_binary(version, commit, True)
190
191
192@cli.command("update", help="Update a single major release")
193@click.option("-v", "--version", required=True, type=str, help="The major version, e.g. '23'")
194@click.option("-c", "--commit", is_flag=True, default=False, help="Commit the result")
195def update(version: str, commit: bool) -> None:
196 update_binary(version, commit, False)
197 update_binary(version, commit, True)
198
199
200@cli.command("update-all", help="Update all releases at once")
201@click.option("-c", "--commit", is_flag=True, default=False, help="Commit the result")
202def update_all(commit: bool) -> None:
203 # Filter out releases that have reached end-of-life
204 filtered_bin_info = dict(
205 filter(
206 lambda entry: int(entry[0]) in supported_version_range(),
207 load_info_json(BINARY_INFO_JSON).items(),
208 )
209 )
210
211 for major_version, _ in filtered_bin_info.items():
212 update_binary(str(major_version), commit, False)
213 update_binary(str(major_version), commit, True)
214
215
216if __name__ == "__main__":
217 cli()