nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1#! /usr/bin/env nix-shell
2#! nix-shell -i python3 -p python3 python3.pkgs.packaging python3.pkgs.requests python3.pkgs.xmltodict
3import json
4import pathlib
5import logging
6import requests
7import sys
8import xmltodict
9from packaging import version
10
11updates_url = "https://www.jetbrains.com/updates/updates.xml"
12versions_file_path = pathlib.Path(__file__).parent.joinpath("versions.json").resolve()
13
14logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
15
16
17def one_or_more(x):
18 return x if isinstance(x, list) else [x]
19
20
21def download_channels():
22 logging.info("Checking for updates from %s", updates_url)
23 updates_response = requests.get(updates_url)
24 updates_response.raise_for_status()
25 root = xmltodict.parse(updates_response.text)
26 products = root["products"]["product"]
27 return {
28 channel["@name"]: channel
29 for product in products
30 for channel in one_or_more(product["channel"])
31 }
32
33
34def build_version(build):
35 build_number = build["@fullNumber"] if "@fullNumber" in build else build["@number"]
36 return version.parse(build_number)
37
38
39def latest_build(channel):
40 builds = one_or_more(channel["build"])
41 latest = max(builds, key=build_version)
42 return latest
43
44
45def download_sha256(url):
46 url = f"{url}.sha256"
47 download_response = requests.get(url)
48 download_response.raise_for_status()
49 return download_response.content.decode('UTF-8').split(' ')[0]
50
51
52channels = download_channels()
53
54
55def update_product(name, product):
56 update_channel = product["update-channel"]
57 logging.info("Updating %s", name)
58 channel = channels.get(update_channel)
59 if channel is None:
60 logging.error("Failed to find channel %s.", update_channel)
61 logging.error("Check that the update-channel in %s matches the name in %s", versions_file_path, updates_url)
62 else:
63 try:
64 build = latest_build(channel)
65 new_version = build["@version"]
66 new_build_number = build["@fullNumber"]
67 if "EAP" not in channel["@name"]:
68 version_or_build_number = new_version
69 else:
70 version_or_build_number = new_build_number
71 version_number = new_version.split(' ')[0]
72 download_url = product["url-template"].format(version=version_or_build_number, versionMajorMinor=version_number)
73 product["url"] = download_url
74 if "sha256" not in product or product.get("build_number") != new_build_number:
75 logging.info("Found a newer version %s with build number %s.", new_version, new_build_number)
76 product["version"] = new_version
77 product["build_number"] = new_build_number
78 product["sha256"] = download_sha256(download_url)
79 else:
80 logging.info("Already at the latest version %s with build number %s.", new_version, new_build_number)
81 except Exception as e:
82 logging.exception("Update failed:", exc_info=e)
83 logging.warning("Skipping %s due to the above error.", name)
84 logging.warning("It may be out-of-date. Fix the error and rerun.")
85
86
87def update_products(products):
88 for name, product in products.items():
89 update_product(name, product)
90
91
92with open(versions_file_path, "r") as versions_file:
93 versions = json.load(versions_file)
94
95for products in versions.values():
96 update_products(products)
97
98with open(versions_file_path, "w") as versions_file:
99 json.dump(versions, versions_file, indent=2)
100 versions_file.write("\n")