178 lines
5.1 KiB
Python
178 lines
5.1 KiB
Python
|
#! /usr/bin/env nix-shell
|
||
|
#! nix-shell -i python3 -p python3 python3.pkgs.semver nix-prefetch-github
|
||
|
from urllib.request import Request, urlopen
|
||
|
import dataclasses
|
||
|
import subprocess
|
||
|
import hashlib
|
||
|
import os.path
|
||
|
import semver
|
||
|
import base64
|
||
|
from typing import (
|
||
|
Optional,
|
||
|
Dict,
|
||
|
List,
|
||
|
)
|
||
|
import json
|
||
|
import os
|
||
|
|
||
|
|
||
|
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||
|
NIXPKGS = os.path.abspath(os.path.join(SCRIPT_DIR, "../../../../"))
|
||
|
|
||
|
|
||
|
OWNER = "LemmyNet"
|
||
|
UI_REPO = "lemmy-ui"
|
||
|
SERVER_REPO = "lemmy"
|
||
|
|
||
|
|
||
|
@dataclasses.dataclass
|
||
|
class Pin:
|
||
|
serverVersion: str
|
||
|
uiVersion: str
|
||
|
serverSha256: str = ""
|
||
|
serverCargoSha256: str = ""
|
||
|
uiSha256: str = ""
|
||
|
uiYarnDepsSha256: str = ""
|
||
|
|
||
|
filename: Optional[str] = None
|
||
|
|
||
|
def write(self) -> None:
|
||
|
if not self.filename:
|
||
|
raise ValueError("No filename set")
|
||
|
|
||
|
with open(self.filename, "w") as fd:
|
||
|
pin = dataclasses.asdict(self)
|
||
|
del pin["filename"]
|
||
|
json.dump(pin, fd, indent=2)
|
||
|
fd.write("\n")
|
||
|
|
||
|
|
||
|
def github_get(path: str) -> Dict:
|
||
|
"""Send a GET request to Gituhb, optionally adding GITHUB_TOKEN auth header"""
|
||
|
url = f"https://api.github.com/{path.lstrip('/')}"
|
||
|
print(f"Retreiving {url}")
|
||
|
|
||
|
req = Request(url)
|
||
|
|
||
|
if "GITHUB_TOKEN" in os.environ:
|
||
|
req.add_header("authorization", f"Bearer {os.environ['GITHUB_TOKEN']}")
|
||
|
|
||
|
with urlopen(req) as resp:
|
||
|
return json.loads(resp.read())
|
||
|
|
||
|
|
||
|
def get_latest_release(owner: str, repo: str) -> str:
|
||
|
return github_get(f"/repos/{owner}/{repo}/releases/latest")["tag_name"]
|
||
|
|
||
|
|
||
|
def sha256_url(url: str) -> str:
|
||
|
sha256 = hashlib.sha256()
|
||
|
with urlopen(url) as resp:
|
||
|
while data := resp.read(1024):
|
||
|
sha256.update(data)
|
||
|
return "sha256-" + base64.urlsafe_b64encode(sha256.digest()).decode()
|
||
|
|
||
|
|
||
|
def prefetch_github(owner: str, repo: str, rev: str) -> str:
|
||
|
"""Prefetch github rev and return sha256 hash"""
|
||
|
print(f"Prefetching {owner}/{repo}({rev})")
|
||
|
|
||
|
proc = subprocess.run(
|
||
|
["nix-prefetch-github", owner, repo, "--rev", rev, "--fetch-submodules"],
|
||
|
check=True,
|
||
|
stdout=subprocess.PIPE,
|
||
|
)
|
||
|
|
||
|
sha256 = json.loads(proc.stdout)["sha256"]
|
||
|
if not sha256.startswith("sha256-"): # Work around bug in nix-prefetch-github
|
||
|
return "sha256-" + sha256
|
||
|
|
||
|
return sha256
|
||
|
|
||
|
|
||
|
def get_latest_tag(owner: str, repo: str, prerelease: bool = False) -> str:
|
||
|
"""Get the latest tag from a Github Repo"""
|
||
|
tags: List[str] = []
|
||
|
|
||
|
# As the Github API doesn't have any notion of "latest" for tags we need to
|
||
|
# collect all of them and sort so we can figure out the latest one.
|
||
|
i = 0
|
||
|
while i <= 100: # Prevent infinite looping
|
||
|
i += 1
|
||
|
resp = github_get(f"/repos/{owner}/{repo}/tags?page={i}")
|
||
|
if not resp:
|
||
|
break
|
||
|
|
||
|
# Filter out unparseable tags
|
||
|
for tag in resp:
|
||
|
try:
|
||
|
parsed = semver.Version.parse(tag["name"])
|
||
|
if (
|
||
|
semver.Version.parse(tag["name"])
|
||
|
and not prerelease
|
||
|
and parsed.prerelease
|
||
|
): # Filter out release candidates
|
||
|
continue
|
||
|
except ValueError:
|
||
|
continue
|
||
|
else:
|
||
|
tags.append(tag["name"])
|
||
|
|
||
|
# Sort and return latest
|
||
|
return sorted(tags, key=lambda name: semver.Version.parse(name))[-1]
|
||
|
|
||
|
|
||
|
def get_fod_hash(attr: str) -> str:
|
||
|
"""
|
||
|
Get fixed output hash for attribute.
|
||
|
This depends on a fixed output derivation with an empty hash.
|
||
|
"""
|
||
|
|
||
|
print(f"Getting fixed output hash for {attr}")
|
||
|
|
||
|
proc = subprocess.run(["nix-build", NIXPKGS, "-A", attr], stderr=subprocess.PIPE)
|
||
|
if proc.returncode != 1:
|
||
|
raise ValueError("Expected nix-build to fail")
|
||
|
|
||
|
# Iterate list in reverse order so we get the "got:" line early
|
||
|
for line in proc.stderr.decode().split("\n")[::-1]:
|
||
|
cols = line.split()
|
||
|
if cols and cols[0] == "got:":
|
||
|
return cols[1]
|
||
|
|
||
|
raise ValueError("No fixed output hash found")
|
||
|
|
||
|
|
||
|
def make_server_pin(pin: Pin, attr: str) -> None:
|
||
|
pin.serverSha256 = prefetch_github(OWNER, SERVER_REPO, pin.serverVersion)
|
||
|
pin.write()
|
||
|
pin.serverCargoSha256 = get_fod_hash(attr)
|
||
|
pin.write()
|
||
|
|
||
|
|
||
|
def make_ui_pin(pin: Pin, package_json: str, attr: str) -> None:
|
||
|
# Save a copy of package.json
|
||
|
print("Getting package.json")
|
||
|
with urlopen(
|
||
|
f"https://raw.githubusercontent.com/{OWNER}/{UI_REPO}/{pin.uiVersion}/package.json"
|
||
|
) as resp:
|
||
|
with open(os.path.join(SCRIPT_DIR, package_json), "wb") as fd:
|
||
|
fd.write(resp.read())
|
||
|
|
||
|
pin.uiSha256 = prefetch_github(OWNER, UI_REPO, pin.uiVersion)
|
||
|
pin.write()
|
||
|
pin.uiYarnDepsSha256 = get_fod_hash(attr)
|
||
|
pin.write()
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
# Get server version
|
||
|
server_version = get_latest_tag(OWNER, SERVER_REPO)
|
||
|
|
||
|
# Get UI version (not always the same as lemmy-server)
|
||
|
ui_version = get_latest_tag(OWNER, UI_REPO)
|
||
|
|
||
|
pin = Pin(server_version, ui_version, filename=os.path.join(SCRIPT_DIR, "pin.json"))
|
||
|
make_server_pin(pin, "lemmy-server")
|
||
|
make_ui_pin(pin, "package.json", "lemmy-ui")
|