Merge pull request #330634 from r-ryantm/auto-update/circumflex
[NixPkgs.git] / pkgs / servers / web-apps / lemmy / update.py
blobc9ce4e6e8823c7d8472c4fb4e800b6922d2827a7
1 #! /usr/bin/env nix-shell
2 #! nix-shell -i python3 -p python3 python3.pkgs.semver nix-prefetch-github
3 from urllib.request import Request, urlopen
4 import dataclasses
5 import subprocess
6 import os.path
7 import semver
8 from typing import (
9 Optional,
10 Dict,
11 List,
13 import json
14 import os
17 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
18 NIXPKGS = os.path.abspath(os.path.join(SCRIPT_DIR, "../../../../"))
21 OWNER = "LemmyNet"
22 UI_REPO = "lemmy-ui"
23 SERVER_REPO = "lemmy"
26 @dataclasses.dataclass
27 class Pin:
28 serverVersion: str
29 uiVersion: str
30 serverHash: str = ""
31 serverCargoHash: str = ""
32 uiHash: str = ""
33 uiPNPMDepsHash: str = ""
35 filename: Optional[str] = None
37 def write(self) -> None:
38 if not self.filename:
39 raise ValueError("No filename set")
41 with open(self.filename, "w") as fd:
42 pin = dataclasses.asdict(self)
43 del pin["filename"]
44 json.dump(pin, fd, indent=2)
45 fd.write("\n")
48 def github_get(path: str) -> Dict:
49 """Send a GET request to GitHub, optionally adding GITHUB_TOKEN auth header"""
50 url = f"https://api.github.com/{path.lstrip('/')}"
51 print(f"Retrieving {url}")
53 req = Request(url)
55 if "GITHUB_TOKEN" in os.environ:
56 req.add_header("authorization", f"Bearer {os.environ['GITHUB_TOKEN']}")
58 with urlopen(req) as resp:
59 return json.loads(resp.read())
62 def get_latest_release(owner: str, repo: str) -> str:
63 return github_get(f"/repos/{owner}/{repo}/releases/latest")["tag_name"]
66 def prefetch_github(owner: str, repo: str, rev: str) -> str:
67 """Prefetch GitHub rev and return SRI hash"""
68 print(f"Prefetching {owner}/{repo}({rev})")
70 proc = subprocess.run(
71 ["nix-prefetch-github", owner, repo, "--rev", rev, "--fetch-submodules"],
72 check=True,
73 stdout=subprocess.PIPE,
76 return json.loads(proc.stdout)["hash"]
79 def get_latest_tag(owner: str, repo: str, prerelease: bool = False) -> str:
80 """Get the latest tag from a GitHub Repo"""
81 tags: List[str] = []
83 # As the GitHub API doesn't have any notion of "latest" for tags we need to
84 # collect all of them and sort so we can figure out the latest one.
85 i = 0
86 while i <= 100: # Prevent infinite looping
87 i += 1
88 resp = github_get(f"/repos/{owner}/{repo}/tags?page={i}")
89 if not resp:
90 break
92 # Filter out unparseable tags
93 for tag in resp:
94 try:
95 parsed = semver.Version.parse(tag["name"])
96 if (
97 semver.Version.parse(tag["name"])
98 and not prerelease
99 and parsed.prerelease
100 ): # Filter out release candidates
101 continue
102 except ValueError:
103 continue
104 else:
105 tags.append(tag["name"])
107 # Sort and return latest
108 return sorted(tags, key=lambda name: semver.Version.parse(name))[-1]
111 def get_fod_hash(attr: str) -> str:
113 Get fixed output hash for attribute.
114 This depends on a fixed output derivation with an empty hash.
117 print(f"Getting fixed output hash for {attr}")
119 proc = subprocess.run(["nix-build", NIXPKGS, "-A", attr], stderr=subprocess.PIPE)
120 if proc.returncode != 1:
121 raise ValueError("Expected nix-build to fail")
123 # Iterate list in reverse order so we get the "got:" line early
124 for line in proc.stderr.decode().split("\n")[::-1]:
125 cols = line.split()
126 if cols and cols[0] == "got:":
127 return cols[1]
129 raise ValueError("No fixed output hash found")
132 def make_server_pin(pin: Pin, attr: str) -> None:
133 pin.serverHash = prefetch_github(OWNER, SERVER_REPO, pin.serverVersion)
134 pin.write()
135 pin.serverCargoHash = get_fod_hash(attr)
136 pin.write()
139 def make_ui_pin(pin: Pin, attr: str) -> None:
140 pin.uiHash = prefetch_github(OWNER, UI_REPO, pin.uiVersion)
141 pin.write()
142 pin.uiPNPMDepsHash = get_fod_hash(attr)
143 pin.write()
146 if __name__ == "__main__":
147 # Get server version
148 server_version = get_latest_tag(OWNER, SERVER_REPO)
150 # Get UI version (not always the same as lemmy-server)
151 ui_version = get_latest_tag(OWNER, UI_REPO)
153 pin = Pin(server_version, ui_version, filename=os.path.join(SCRIPT_DIR, "pin.json"))
154 make_server_pin(pin, "lemmy-server")
155 make_ui_pin(pin, "lemmy-ui")