mirror of
https://github.com/NixOS/nixpkgs.git
synced 2026-10-04 05:50:00 +00:00
papermc: migrate updateScript to paper's v3 api
This commit is contained in:
@@ -1,13 +1,23 @@
|
||||
let
|
||||
versions = builtins.fromJSON (builtins.readFile ./versions.json);
|
||||
in
|
||||
{ callPackage, lib, ... }:
|
||||
{
|
||||
callPackage,
|
||||
javaPackages,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
latestVersion = lib.last (builtins.sort lib.versionOlder (builtins.attrNames versions));
|
||||
escapeVersion = builtins.replaceStrings [ "." ] [ "_" ];
|
||||
getJavaVersion = v: (builtins.getAttr "openjdk${toString v}" javaPackages.compiler).headless;
|
||||
|
||||
packages = lib.mapAttrs' (version: value: {
|
||||
name = "papermc-${escapeVersion version}";
|
||||
value = callPackage ./derivation.nix { inherit (value) version hash; };
|
||||
value = callPackage ./derivation.nix {
|
||||
inherit (value) url version hash;
|
||||
jre = getJavaVersion value.javaVersion;
|
||||
};
|
||||
}) versions;
|
||||
in
|
||||
lib.recurseIntoAttrs (
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
jre,
|
||||
version,
|
||||
hash,
|
||||
url,
|
||||
udev,
|
||||
}:
|
||||
|
||||
@@ -13,16 +14,9 @@ stdenvNoCC.mkDerivation (finalAttrs: {
|
||||
pname = "papermc";
|
||||
inherit version hash;
|
||||
|
||||
src =
|
||||
let
|
||||
version-split = lib.strings.splitString "-" finalAttrs.version;
|
||||
mcVersion = builtins.elemAt version-split 0;
|
||||
buildNum = builtins.elemAt version-split 1;
|
||||
in
|
||||
fetchurl {
|
||||
url = "https://api.papermc.io/v2/projects/paper/versions/${mcVersion}/builds/${buildNum}/downloads/paper-${mcVersion}-${buildNum}.jar";
|
||||
inherit (finalAttrs) hash;
|
||||
};
|
||||
src = fetchurl {
|
||||
inherit url hash;
|
||||
};
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
@@ -45,7 +39,10 @@ stdenvNoCC.mkDerivation (finalAttrs: {
|
||||
allowSubstitutes = false;
|
||||
|
||||
passthru = {
|
||||
updateScript = ./update.py;
|
||||
updateScript = {
|
||||
command = [ ./update.py ];
|
||||
supportedFeatures = [ "commit" ];
|
||||
};
|
||||
};
|
||||
|
||||
meta = {
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#! nix-shell -i python -p "python3.withPackages (ps: with ps; [ps.requests ])"
|
||||
|
||||
import os
|
||||
import hashlib
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import requests
|
||||
|
||||
HEADERS = {"User-Agent": "nixpkgs/1.0.0 https://github.com/nixos/nixpkgs"}
|
||||
|
||||
|
||||
class Version:
|
||||
def __init__(self, name: str):
|
||||
self.name: str = name
|
||||
self.hash: str | None = None
|
||||
self.build_number: int | None = None
|
||||
self.url: str | None = None
|
||||
self.java_version: int | None = None
|
||||
|
||||
@property
|
||||
def full_name(self):
|
||||
@@ -27,7 +33,7 @@ class Version:
|
||||
|
||||
|
||||
class VersionManager:
|
||||
def __init__(self, base_url: str = "https://api.papermc.io/v2/projects/paper"):
|
||||
def __init__(self, base_url: str = "https://fill.papermc.io/v3/projects/paper"):
|
||||
self.versions: list[Version] = []
|
||||
self.base_url: str = base_url
|
||||
|
||||
@@ -36,7 +42,8 @@ class VersionManager:
|
||||
Fetch all versions after given minor release
|
||||
"""
|
||||
|
||||
response = requests.get(self.base_url)
|
||||
url = f"{self.base_url}/versions"
|
||||
response = requests.get(url, headers=HEADERS)
|
||||
|
||||
try:
|
||||
response.raise_for_status()
|
||||
@@ -45,19 +52,24 @@ class VersionManager:
|
||||
print(e)
|
||||
return
|
||||
|
||||
# we only want versions that are no pre-releases
|
||||
release_versions = filter(
|
||||
lambda v_name: all(s not in v_name for s in ["pre", "rc"]), response.json()["versions"])
|
||||
release_versions = response.json()["versions"]
|
||||
|
||||
for version_name in release_versions:
|
||||
version_id = version_name["version"]["id"]
|
||||
|
||||
# we only want versions that are not pre-releases
|
||||
if ("pre" in version_id) or ("rc" in version_id):
|
||||
continue
|
||||
|
||||
# split version string, convert to list ot int
|
||||
version_split = version_name.split(".")
|
||||
version_split = version_id.split(".")
|
||||
version_split = list(map(int, version_split))
|
||||
|
||||
# check if version is higher than 1.<not_before_sub_version>
|
||||
if (version_split[0] > 1) or (version_split[0] == 1 and version_split[1] >= not_before_minor_version):
|
||||
self.versions.append(Version(version_name))
|
||||
if (version_split[0] > 1) or (
|
||||
version_split[0] == 1 and version_split[1] >= not_before_minor_version
|
||||
):
|
||||
self.versions.append(Version(version_id))
|
||||
|
||||
def fetch_latest_version_builds(self):
|
||||
"""
|
||||
@@ -65,8 +77,60 @@ class VersionManager:
|
||||
"""
|
||||
|
||||
for version in self.versions:
|
||||
url = f"{self.base_url}/versions/{version.name}"
|
||||
response = requests.get(url)
|
||||
body = self.fetch_latest_build(version.name)
|
||||
|
||||
# the latest build from the api
|
||||
latest_build = body["id"]
|
||||
version.build_number = latest_build
|
||||
|
||||
version.java_version = self.fetch_java_version(version.name)
|
||||
|
||||
# Grab the url from the api
|
||||
download_info = body["downloads"]["server:default"]
|
||||
version.url = download_info["url"]
|
||||
|
||||
hex_hash = download_info["checksums"]["sha256"]
|
||||
raw_bytes = bytes.fromhex(hex_hash)
|
||||
|
||||
base64_encoded = base64.b64encode(raw_bytes).decode("utf-8")
|
||||
|
||||
version.hash = f"sha256-{base64_encoded}"
|
||||
|
||||
def fetch_latest_build(self, name: str) -> dict[str, Any]:
|
||||
url = f"{self.base_url}/versions/{name}/builds/latest"
|
||||
response = requests.get(url, headers=HEADERS)
|
||||
|
||||
# check that we've got a good response
|
||||
try:
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except requests.exceptions.HTTPError as e:
|
||||
print(e)
|
||||
return {}
|
||||
|
||||
def fetch_java_version(self, name: str):
|
||||
url = f"{self.base_url}/versions/{name}"
|
||||
response = requests.get(url, headers=HEADERS)
|
||||
|
||||
# check that we've got a good response
|
||||
try:
|
||||
response.raise_for_status()
|
||||
return response.json()["version"]["java"]["version"]["minimum"]
|
||||
except requests.exceptions.HTTPError as e:
|
||||
print(e)
|
||||
return
|
||||
|
||||
def generate_version_hashes(self):
|
||||
"""
|
||||
Fetch and set the hashes for all registered versions (versions are downloaded to memory)
|
||||
"""
|
||||
|
||||
print("Fetching version hashes")
|
||||
for version in self.versions:
|
||||
url = (
|
||||
f"{self.base_url}/versions/{version.name}/builds/{version.build_number}"
|
||||
)
|
||||
response = requests.get(url, headers=HEADERS)
|
||||
|
||||
# check that we've got a good response
|
||||
try:
|
||||
@@ -76,77 +140,129 @@ class VersionManager:
|
||||
print(e)
|
||||
return
|
||||
|
||||
# the highest build in response.json()['builds']:
|
||||
latest_build = response.json()['builds'][-1]
|
||||
version.build_number = latest_build
|
||||
def versions_to_dict(self) -> dict[str, Any]:
|
||||
return {
|
||||
version.name: {
|
||||
"hash": version.hash,
|
||||
"version": version.full_name,
|
||||
"url": version.url,
|
||||
"javaVersion": version.java_version,
|
||||
}
|
||||
for version in self.versions
|
||||
}
|
||||
|
||||
def generate_version_hashes(self):
|
||||
"""
|
||||
Generate and set the hashes for all registered versions (versions will are downloaded to memory)
|
||||
"""
|
||||
|
||||
for version in self.versions:
|
||||
url = f"{self.base_url}/versions/{version.name}/builds/{version.build_number}/downloads/paper-{version.full_name}.jar"
|
||||
version.hash = self.download_and_generate_sha256_hash(url)
|
||||
|
||||
def versions_to_json(self):
|
||||
return json.dumps(
|
||||
{version.name: {'hash': version.hash, 'version': version.full_name}
|
||||
for version in self.versions},
|
||||
indent=4
|
||||
)
|
||||
def generate_version_dict(self) -> dict[str, Any]:
|
||||
self.fetch_versions()
|
||||
self.fetch_latest_version_builds()
|
||||
return self.versions_to_dict()
|
||||
|
||||
@staticmethod
|
||||
def find_version_json() -> str:
|
||||
"""
|
||||
Find the versions.json file in the same directory as this script
|
||||
"""
|
||||
return os.path.join(os.path.dirname(os.path.realpath(__file__)), "versions.json")
|
||||
|
||||
def write_versions(self, file_name: str = find_version_json()):
|
||||
""" write all processed versions to json """
|
||||
# save json to versions.json
|
||||
with open(file_name, 'w') as f:
|
||||
f.write(self.versions_to_json() + "\n")
|
||||
|
||||
@staticmethod
|
||||
def download_and_generate_sha256_hash(url: str) -> str | None:
|
||||
"""
|
||||
Fetch the tarball from the given URL.
|
||||
Then generate a sha256 hash of the tarball.
|
||||
"""
|
||||
|
||||
try:
|
||||
# Download the file from the URL
|
||||
response = requests.get(url)
|
||||
response.raise_for_status()
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Error: {e}")
|
||||
return None
|
||||
|
||||
# Create a new SHA-256 hash object
|
||||
sha256_hash = hashlib.sha256()
|
||||
|
||||
# Update the hash object with chunks of the downloaded content
|
||||
for byte_block in response.iter_content(4096):
|
||||
sha256_hash.update(byte_block)
|
||||
|
||||
# Get the hexadecimal representation of the hash
|
||||
hash_value = sha256_hash.digest()
|
||||
|
||||
# Encode the hash value in base64
|
||||
base64_hash = base64.b64encode(hash_value).decode('utf-8')
|
||||
|
||||
# Format it as "sha256-{base64_hash}"
|
||||
sri_representation = f"sha256-{base64_hash}"
|
||||
|
||||
return sri_representation
|
||||
return os.path.join(
|
||||
os.path.dirname(os.path.realpath(__file__)), "versions.json"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
def get_latest(servers: dict[str, Any]) -> str | None:
|
||||
return max(
|
||||
(v.get("version") for v in servers.values()),
|
||||
key=lambda x: tuple(map(int, re.split("[\\.|-]", x))) if x is not None else (),
|
||||
)
|
||||
|
||||
|
||||
def slugify(version: str) -> str:
|
||||
return version.replace(".", "_")
|
||||
|
||||
|
||||
def generate_commit(
|
||||
previous_servers: dict[str, Any],
|
||||
servers: dict[str, Any],
|
||||
versions_file: Path,
|
||||
) -> list[dict[str, str | list[str]]]:
|
||||
actions = []
|
||||
commit_body_lines = []
|
||||
|
||||
old_latest = get_latest(previous_servers)
|
||||
new_latest = get_latest(servers)
|
||||
|
||||
for major_version, server in servers.items():
|
||||
version = server.get("version")
|
||||
previous_server = previous_servers.get(major_version)
|
||||
|
||||
if version is None:
|
||||
continue
|
||||
|
||||
attribute = f"papermcServers.papermc-{slugify(major_version)}"
|
||||
|
||||
if not previous_server:
|
||||
# this version didn't exist before
|
||||
# check if its now the latest version
|
||||
if version == new_latest:
|
||||
action = f"{old_latest} -> {new_latest}"
|
||||
attribute = "papermc"
|
||||
else:
|
||||
action = f"init {version}"
|
||||
|
||||
else:
|
||||
previous_version = previous_server.get("version")
|
||||
if previous_version == version:
|
||||
continue
|
||||
|
||||
action = f"{previous_version} -> {version}"
|
||||
|
||||
actions.append(action)
|
||||
|
||||
commit_body_lines.append(f"{attribute}: {action}")
|
||||
|
||||
if not commit_body_lines:
|
||||
return []
|
||||
|
||||
if len(actions) == 1:
|
||||
commit_message = commit_body_lines[0]
|
||||
|
||||
# the body should only be the release notes to avoid repetition
|
||||
# if the release notes don't exist this will be blank
|
||||
commit_body = "\n".join(commit_body_lines[1:]).strip()
|
||||
else:
|
||||
detailed_message = f"papermc: {', '.join(actions)}"
|
||||
|
||||
commit_message = (
|
||||
detailed_message
|
||||
if len(detailed_message) <= 72
|
||||
else "papermc: update multiple versions"
|
||||
)
|
||||
|
||||
commit_body = "\n".join(commit_body_lines).strip()
|
||||
|
||||
commit_json = {
|
||||
"attrPath": "papermcServers.papermc",
|
||||
"files": [str(versions_file)],
|
||||
"commitMessage": commit_message,
|
||||
}
|
||||
|
||||
if commit_body:
|
||||
commit_json["commitBody"] = commit_body
|
||||
|
||||
return [commit_json]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
versions_file = Path(__file__).parent / "versions.json"
|
||||
|
||||
with open(versions_file, "r") as f:
|
||||
old_versions = json.load(f)
|
||||
|
||||
version_manager = VersionManager()
|
||||
|
||||
version_manager.fetch_versions()
|
||||
version_manager.fetch_latest_version_builds()
|
||||
version_manager.generate_version_hashes()
|
||||
version_manager.write_versions()
|
||||
new_versions = version_manager.generate_version_dict()
|
||||
|
||||
commit_json = generate_commit(old_versions, new_versions, versions_file)
|
||||
|
||||
with open(versions_file, "w") as f:
|
||||
json.dump(new_versions, f, indent=4)
|
||||
f.write("\n")
|
||||
|
||||
print(json.dumps(commit_json))
|
||||
|
||||
Reference in New Issue
Block a user