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
|
import json
import re
import sys
import subprocess
import urllib.request
from typing import Iterable, Optional, Tuple
from urllib.request import urlopen
from datetime import datetime
# Number of spaces used for each indentation level
JSON_INDENT = 4
releases_json = None
# Releases that have reached end-of-life no longer receive any updates
# and it is rather pointless trying to update those.
#
# https://endoflife.date/electron
def supported_version_range() -> range:
"""Returns a range of electron releases that have not reached end-of-life yet"""
global releases_json
if releases_json is None:
releases_json = json.loads(
urlopen("https://endoflife.date/api/electron.json").read()
)
supported_releases = [
int(x["cycle"])
for x in releases_json
if x["eol"] == False
or datetime.strptime(x["eol"], "%Y-%m-%d") > datetime.today()
]
return range(
min(supported_releases), # incl.
# We have also packaged the beta release in nixpkgs,
# but it is not tracked by endoflife.date
max(supported_releases) + 2, # excl.
1,
)
def get_latest_version(major_version: str) -> Tuple[str, str]:
"""Returns the latest version for a given major version"""
electron_releases: dict = json.loads(
urlopen("https://releases.electronjs.org/releases.json").read()
)
major_version_releases = filter(
lambda item: item["version"].startswith(f"{major_version}."), electron_releases
)
m = max(major_version_releases, key=lambda item: item["date"])
rev = f"v{m['version']}"
return (m, rev)
def load_info_json(path: str) -> dict:
"""Load the contents of a JSON file
Args:
path: The path to the JSON file
Returns: An empty dict if the path does not exist, otherwise the contents of the JSON file.
"""
try:
with open(path, "r") as f:
return json.loads(f.read())
except:
return {}
def save_info_json(path: str, content: dict) -> None:
"""Saves the given info to a JSON file
Args:
path: The path where the info should be saved
content: The content to be saved as JSON.
"""
with open(path, "w") as f:
f.write(json.dumps(content, indent=JSON_INDENT, default=vars, sort_keys=True))
f.write("\n")
def parse_cve_numbers(tag_name: str) -> Iterable[str]:
"""Returns mentioned CVE numbers from a given release tag"""
cve_pattern = r"CVE-\d{4}-\d+"
url = f"https://api.github.com/repos/electron/electron/releases/tags/{tag_name}"
headers = {
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
}
request = urllib.request.Request(url=url, headers=headers)
release_note = ""
try:
with urlopen(request) as response:
release_note = json.loads(response.read().decode("utf-8"))["body"]
except:
print(
f"WARN: Fetching release note for {tag_name} from GitHub failed!",
file=sys.stderr,
)
return sorted(re.findall(cve_pattern, release_note))
def commit_result(
package_name: str, old_version: Optional[str], new_version: str, path: str
) -> None:
"""Creates a git commit with a short description of the change
Args:
package_name: The package name, e.g. `electron-source.electron-{major_version}`
or `electron_{major_version}-bin`
old_version: Version number before the update.
Can be left empty when initializing a new release.
new_version: Version number after the update.
path: Path to the lockfile to be committed
"""
assert (
isinstance(package_name, str) and len(package_name) > 0
), "Argument `package_name` cannot be empty"
assert (
isinstance(new_version, str) and len(new_version) > 0
), "Argument `new_version` cannot be empty"
if old_version != new_version:
major_version = new_version.split(".")[0]
cve_fixes_text = "\n".join(
list(
map(lambda cve: f"- Fixes {cve}", parse_cve_numbers(f"v{new_version}"))
)
)
init_msg = f"init at {new_version}"
update_msg = f"{old_version} -> {new_version}"
diff = (
f"- Diff: https://github.com/electron/electron/compare/refs/tags/v{old_version}...v{new_version}\n"
if old_version != None
else ""
)
commit_message = f"""{package_name}: {update_msg if old_version != None else init_msg}
- Changelog: https://github.com/electron/electron/releases/tag/v{new_version}
{diff}{cve_fixes_text}
"""
subprocess.run(
[
"git",
"add",
path,
]
)
subprocess.run(
[
"git",
"commit",
"-m",
commit_message,
]
)
|