Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
at devShellTools-shell 159 lines 5.2 kB view raw
1#!/usr/bin/env nix-shell 2#!nix-shell -i python3 -p python3Packages.requests python3Packages.dataclasses-json 3 4import json 5from dataclasses import dataclass, field 6from datetime import datetime 7from pathlib import Path 8from typing import Any, Dict, List, Optional 9 10import requests 11from dataclasses_json import DataClassJsonMixin, LetterCase, config 12from marshmallow import fields 13 14 15@dataclass 16class Download(DataClassJsonMixin): 17 sha1: str 18 size: int 19 url: str 20 21 22@dataclass 23class Version(DataClassJsonMixin): 24 id: str 25 type: str 26 url: str 27 time: datetime = field( 28 metadata=config( 29 encoder=datetime.isoformat, 30 decoder=datetime.fromisoformat, 31 mm_field=fields.DateTime(format="iso"), 32 ) 33 ) 34 release_time: datetime = field( 35 metadata=config( 36 encoder=datetime.isoformat, 37 decoder=datetime.fromisoformat, 38 mm_field=fields.DateTime(format="iso"), 39 letter_case=LetterCase.CAMEL, 40 ) 41 ) 42 43 def get_manifest(self) -> Any: 44 """Return the version's manifest.""" 45 response = requests.get(self.url) 46 response.raise_for_status() 47 return response.json() 48 49 def get_downloads(self) -> Dict[str, Download]: 50 """ 51 Return all downloadable files from the version's manifest, in Download 52 objects. 53 """ 54 return { 55 download_name: Download.from_dict(download_info) 56 for download_name, download_info in self.get_manifest()["downloads"].items() 57 } 58 59 def get_java_version(self) -> Any: 60 """ 61 Return the java version specified in a version's manifest, if it is 62 present. Versions <= 1.6 do not specify this. 63 """ 64 return self.get_manifest().get("javaVersion", {}).get("majorVersion", None) 65 66 def get_server(self) -> Optional[Download]: 67 """ 68 If the version has a server download available, return the Download 69 object for the server download. If the version does not have a server 70 download avilable, return None. 71 """ 72 downloads = self.get_downloads() 73 if "server" in downloads: 74 return downloads["server"] 75 return None 76 77 78def get_versions() -> List[Version]: 79 """Return a list of Version objects for all available versions.""" 80 response = requests.get( 81 "https://launchermeta.mojang.com/mc/game/version_manifest.json" 82 ) 83 response.raise_for_status() 84 data = response.json() 85 return [Version.from_dict(version) for version in data["versions"]] 86 87 88def get_major_release(version_id: str) -> str: 89 """ 90 Return the major release for a version. The major release for 1.17 and 91 1.17.1 is 1.17. 92 """ 93 if not len(version_id.split(".")) >= 2: 94 raise ValueError(f"version not in expected format: '{version_id}'") 95 return ".".join(version_id.split(".")[:2]) 96 97 98def group_major_releases(releases: List[Version]) -> Dict[str, List[Version]]: 99 """ 100 Return a dictionary containing each version grouped by each major release. 101 The key "1.17" contains a list with two Version objects, one for "1.17" 102 and another for "1.17.1". 103 """ 104 groups: Dict[str, List[Version]] = {} 105 for release in releases: 106 major_release = get_major_release(release.id) 107 if major_release not in groups: 108 groups[major_release] = [] 109 groups[major_release].append(release) 110 return groups 111 112 113def get_latest_major_releases(releases: List[Version]) -> Dict[str, Version]: 114 """ 115 Return a dictionary containing the latest version for each major release. 116 The latest major release for 1.16 is 1.16.5, so the key "1.16" contains a 117 Version object for 1.16.5. 118 """ 119 return { 120 major_release: max( 121 (release for release in releases if get_major_release(release.id) == major_release), 122 key=lambda x: tuple(map(int, x.id.split('.'))), 123 ) 124 for major_release in group_major_releases(releases) 125 } 126 127 128def generate() -> Dict[str, Dict[str, str]]: 129 """ 130 Return a dictionary containing the latest url, sha1 and version for each major 131 release. 132 """ 133 versions = get_versions() 134 releases = list( 135 filter(lambda version: version.type == "release", versions) 136 ) # remove snapshots and betas 137 latest_major_releases = get_latest_major_releases(releases) 138 139 servers = { 140 version: Download.schema().dump(download_info) # Download -> dict 141 for version, download_info in { 142 version: value.get_server() 143 for version, value in latest_major_releases.items() 144 }.items() 145 if download_info is not None # versions < 1.2 do not have a server 146 } 147 for server in servers.values(): 148 del server["size"] # don't need it 149 150 for version, server in servers.items(): 151 server["version"] = latest_major_releases[version].id 152 server["javaVersion"] = latest_major_releases[version].get_java_version() 153 return servers 154 155 156if __name__ == "__main__": 157 with open(Path(__file__).parent / "versions.json", "w") as file: 158 json.dump(generate(), file, indent=2) 159 file.write("\n")