Files
nixpkgs/pkgs/development/tools/electron/update_util.py
2025-04-05 10:06:16 +02:00

162 lines
4.9 KiB
Python
Executable File

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,
]
)