summary refs log tree commit diff
path: root/pkgs/applications/editors/jetbrains/update.py
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/applications/editors/jetbrains/update.py')
-rwxr-xr-xpkgs/applications/editors/jetbrains/update.py97
1 files changed, 97 insertions, 0 deletions
diff --git a/pkgs/applications/editors/jetbrains/update.py b/pkgs/applications/editors/jetbrains/update.py
new file mode 100755
index 00000000000..5301a85ba9a
--- /dev/null
+++ b/pkgs/applications/editors/jetbrains/update.py
@@ -0,0 +1,97 @@
+#! /usr/bin/env nix-shell
+#! nix-shell -i python3 -p python3 python3.pkgs.packaging python3.pkgs.requests python3.pkgs.xmltodict
+import hashlib
+import json
+import pathlib
+import logging
+import requests
+import sys
+import xmltodict
+from packaging import version
+
+updates_url = "https://www.jetbrains.com/updates/updates.xml"
+versions_file_path = pathlib.Path(__file__).parent.joinpath("versions.json").resolve()
+
+logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
+
+
+def one_or_more(x):
+    return x if isinstance(x, list) else [x]
+
+
+def download_channels():
+    logging.info("Checking for updates from %s", updates_url)
+    updates_response = requests.get(updates_url)
+    updates_response.raise_for_status()
+    root = xmltodict.parse(updates_response.text)
+    products = root["products"]["product"]
+    return {
+        channel["@name"]: channel
+        for product in products
+        for channel in one_or_more(product["channel"])
+    }
+
+
+def build_version(build):
+    return version.parse(build["@version"])
+
+
+def latest_build(channel):
+    builds = one_or_more(channel["build"])
+    latest = max(builds, key=build_version)
+    return latest
+
+
+def download_sha256(url):
+    download_response = requests.get(url)
+    download_response.raise_for_status()
+    h = hashlib.sha256()
+    h.update(download_response.content)
+    return h.hexdigest()
+
+
+channels = download_channels()
+
+
+def update_product(name, product):
+    update_channel = product["update-channel"]
+    logging.info("Updating %s", name)
+    channel = channels.get(update_channel)
+    if channel is None:
+        logging.error("Failed to find channel %s.", update_channel)
+        logging.error("Check that the update-channel in %s matches the name in %s", versions_file_path, updates_url)
+    else:
+        try:
+            build = latest_build(channel)
+            version = build["@version"]
+            parsed_version = build_version(build)
+            version_major_minor = f"{parsed_version.major}.{parsed_version.minor}"
+            download_url = product["url-template"].format(version = version, versionMajorMinor = version_major_minor)
+            product["url"] = download_url
+            product["version-major-minor"] = version_major_minor
+            if "sha256" not in product or product.get("version") != version:
+                logging.info("Found a newer version %s.", version)
+                product["version"] = version
+                product["sha256"] = download_sha256(download_url)
+            else:
+                logging.info("Already at the latest version %s.", version)
+        except Exception as e:
+            logging.exception("Update failed:", exc_info=e)
+            logging.warning("Skipping %s due to the above error.", name)
+            logging.warning("It may be out-of-date. Fix the error and rerun.")
+
+
+def update_products(products):
+    for name, product in products.items():
+        update_product(name, product)
+
+
+with open(versions_file_path, "r") as versions_file:
+    versions = json.load(versions_file)
+
+for products in versions.values():
+    update_products(products)
+
+with open(versions_file_path, "w") as versions_file:
+    json.dump(versions, versions_file, indent=2)
+    versions_file.write("\n")