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
19 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
20 NIXPKGS = os.path.abspath(os.path.join(SCRIPT_DIR, "../../../../"))
28 @dataclasses.dataclass
32 serverSha256: str = ""
33 serverCargoSha256: str = ""
35 uiYarnDepsSha256: str = ""
37 filename: Optional[str] = None
39 def write(self) -> None:
41 raise ValueError("No filename set")
43 with open(self.filename, "w") as fd:
44 pin = dataclasses.asdict(self)
46 json.dump(pin, fd, indent=2)
50 def github_get(path: str) -> Dict:
51 """Send a GET request to Gituhb, optionally adding GITHUB_TOKEN auth header"""
52 url = f"https://api.github.com/{path.lstrip('/')}"
53 print(f"Retreiving {url}")
57 if "GITHUB_TOKEN" in os.environ:
58 req.add_header("authorization", f"Bearer {os.environ['GITHUB_TOKEN']}")
60 with urlopen(req) as resp:
61 return json.loads(resp.read())
64 def get_latest_release(owner: str, repo: str) -> str:
65 return github_get(f"/repos/{owner}/{repo}/releases/latest")["tag_name"]
68 def sha256_url(url: str) -> str:
69 sha256 = hashlib.sha256()
70 with urlopen(url) as resp:
71 while data := resp.read(1024):
73 return "sha256-" + base64.urlsafe_b64encode(sha256.digest()).decode()
76 def prefetch_github(owner: str, repo: str, rev: str) -> str:
77 """Prefetch github rev and return sha256 hash"""
78 print(f"Prefetching {owner}/{repo}({rev})")
80 proc = subprocess.run(
81 ["nix-prefetch-github", owner, repo, "--rev", rev, "--fetch-submodules"],
83 stdout=subprocess.PIPE,
86 sha256 = json.loads(proc.stdout)["sha256"]
87 if not sha256.startswith("sha256-"): # Work around bug in nix-prefetch-github
88 return "sha256-" + sha256
93 def get_latest_tag(owner: str, repo: str, prerelease: bool = False) -> str:
94 """Get the latest tag from a Github Repo"""
97 # As the Github API doesn't have any notion of "latest" for tags we need to
98 # collect all of them and sort so we can figure out the latest one.
100 while i <= 100: # Prevent infinite looping
102 resp = github_get(f"/repos/{owner}/{repo}/tags?page={i}")
106 # Filter out unparseable tags
109 parsed = semver.Version.parse(tag["name"])
111 semver.Version.parse(tag["name"])
113 and parsed.prerelease
114 ): # Filter out release candidates
119 tags.append(tag["name"])
121 # Sort and return latest
122 return sorted(tags, key=lambda name: semver.Version.parse(name))[-1]
125 def get_fod_hash(attr: str) -> str:
127 Get fixed output hash for attribute.
128 This depends on a fixed output derivation with an empty hash.
131 print(f"Getting fixed output hash for {attr}")
133 proc = subprocess.run(["nix-build", NIXPKGS, "-A", attr], stderr=subprocess.PIPE)
134 if proc.returncode != 1:
135 raise ValueError("Expected nix-build to fail")
137 # Iterate list in reverse order so we get the "got:" line early
138 for line in proc.stderr.decode().split("\n")[::-1]:
140 if cols and cols[0] == "got:":
143 raise ValueError("No fixed output hash found")
146 def make_server_pin(pin: Pin, attr: str) -> None:
147 pin.serverSha256 = prefetch_github(OWNER, SERVER_REPO, pin.serverVersion)
149 pin.serverCargoSha256 = get_fod_hash(attr)
153 def make_ui_pin(pin: Pin, package_json: str, attr: str) -> None:
154 # Save a copy of package.json
155 print("Getting package.json")
157 f"https://raw.githubusercontent.com/{OWNER}/{UI_REPO}/{pin.uiVersion}/package.json"
159 with open(os.path.join(SCRIPT_DIR, package_json), "wb") as fd:
160 fd.write(resp.read())
162 pin.uiSha256 = prefetch_github(OWNER, UI_REPO, pin.uiVersion)
164 pin.uiYarnDepsSha256 = get_fod_hash(attr)
168 if __name__ == "__main__":
170 server_version = get_latest_tag(OWNER, SERVER_REPO)
172 # Get UI version (not always the same as lemmy-server)
173 ui_version = get_latest_tag(OWNER, UI_REPO)
175 pin = Pin(server_version, ui_version, filename=os.path.join(SCRIPT_DIR, "pin.json"))
176 make_server_pin(pin, "lemmy-server")
177 make_ui_pin(pin, "package.json", "lemmy-ui")