Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
1#!/usr/bin/env nix-shell 2#!nix-shell -I nixpkgs=./. -i python3 -p common-updater-scripts gnused nix coreutils python312 3""" 4Updater script for the ocis_5-bin package. 5 6This script fetches an HTML table from a specified URL and parses it to determine the release type 7(either "Rolling" or "Production") of a given software version. It uses the built-in urllib.request 8for fetching the HTML content and the built-in html.parser for parsing the HTML. By relying only on 9standard library modules, we avoid dependencies on third-party libraries, which simplifies deployment 10and improves portability. 11""" 12import urllib.request 13import os 14import subprocess 15import json 16import sys 17from datetime import datetime 18from html.parser import HTMLParser 19 20TRACKING_CHANNEL = "Production" # Either Rolling or Production 21 22GITHUB_TOKEN = os.getenv("GITHUB_TOKEN", None) 23 24MAJOR_VERSION = 5 25PKG_NAME = f"ocis_{MAJOR_VERSION}-5" 26 27class TableParser(HTMLParser): 28 def __init__(self, version): 29 super().__init__() 30 self.version = version 31 self.in_td = False 32 self.current_row = [] 33 self.release_type = None 34 self.in_target_row = False 35 36 def handle_starttag(self, tag, attrs): 37 if tag == "td": 38 self.in_td = True 39 40 if tag == "a": 41 href = dict(attrs).get("href", "") 42 if self.version in href: 43 self.in_target_row = True 44 45 def handle_endtag(self, tag): 46 if tag == "td": 47 self.in_td = False 48 49 if tag == "tr" and self.in_target_row: 50 self.release_type = self.current_row[1] 51 self.in_target_row = False 52 53 if tag == "tr": 54 self.current_row = [] 55 56 def handle_data(self, data): 57 if self.in_td: 58 self.current_row.append(data.strip()) 59 60 61def get_release_type(content, version): 62 parser = TableParser(version) 63 parser.feed(content) 64 return parser.release_type 65 66 67def get_all_versions(): 68 """Get versions from GitHub releases with pagination (up to 10 pages).""" 69 versions = [] 70 page = 1 71 max_pages = 10 72 per_page = 30 73 74 while page <= max_pages: 75 url = f"https://api.github.com/repos/owncloud/ocis/releases?page={page}&per_page={per_page}" 76 req = urllib.request.Request(url) 77 78 if GITHUB_TOKEN: 79 req.add_header("Authorization", f"Bearer {GITHUB_TOKEN}") 80 81 req.add_header("Accept", "application/vnd.github.v3+json") 82 req.add_header("User-Agent", "ocis-bin-updater-script") 83 84 with urllib.request.urlopen(req) as response: 85 if response.status != 200: 86 raise Exception(f"HTTP request failed with status {response.status}") 87 88 data = response.read() 89 releases = json.loads(data) 90 91 if not releases: 92 break 93 94 for release in releases: 95 version = release["tag_name"].lstrip("v") 96 published_date = datetime.strptime( 97 release["published_at"], "%Y-%m-%dT%H:%M:%SZ" 98 ) 99 versions.append({"version": version, "published_date": published_date}) 100 101 page += 1 102 103 if len(releases) < per_page: 104 break 105 106 if not versions: 107 raise Exception("No releases found in GitHub API response") 108 109 return versions 110 111 112def get_current_version(): 113 result = subprocess.run( 114 [ 115 "nix-instantiate", 116 "--eval", 117 "-E", 118 f"with import ./. {{}}; {PKG_NAME}.version or (lib.getVersion {PKG_NAME})", 119 ], 120 capture_output=True, 121 text=True, 122 ) 123 result.check_returncode() 124 return result.stdout.strip().strip('"') 125 126 127def get_hash(os_name, arch, version): 128 url = f"https://github.com/owncloud/ocis/releases/download/v{version}/ocis-{version}-{os_name}-{arch}" 129 result = subprocess.run( 130 ["nix-prefetch-url", "--type", "sha256", url], capture_output=True, text=True 131 ) 132 result.check_returncode() 133 pkg_hash = result.stdout.strip() 134 result = subprocess.run( 135 ["nix", "hash", "to-sri", f"sha256:{pkg_hash}"], capture_output=True, text=True 136 ) 137 result.check_returncode() 138 return result.stdout.strip() 139 140 141def update_source_version(pkg_name, version, hash_value, system): 142 subprocess.run( 143 [ 144 "update-source-version", 145 pkg_name, 146 version, 147 hash_value, 148 f"--system={system}", 149 "--ignore-same-version", 150 ], 151 check=True, 152 ) 153 154 155def main(): 156 print("Fetching all versions from GitHub API (with pagination)...") 157 all_versions = get_all_versions() 158 print(f"Found {len(all_versions)} versions across multiple pages") 159 160 if not all_versions: 161 print("Error: No versions fetched from GitHub API") 162 sys.exit(1) 163 164 # We depend on the fact that versions are sorted reverse chronologically 165 for version in all_versions: 166 if version["version"].startswith(str(MAJOR_VERSION)): 167 latest_version = version 168 break 169 print(f"Latest version from GitHub: {latest_version['version']}") 170 171 nix_current_version = get_current_version() 172 print(f"Current nix version: {nix_current_version}") 173 174 current_version = None 175 for version in all_versions: 176 if nix_current_version == version["version"]: 177 current_version = version 178 break 179 180 if not current_version: 181 available_versions = [v["version"] for v in all_versions] 182 print( 183 f"Error: Cannot find GitHub release for current nix version {nix_current_version}" 184 ) 185 print( 186 f"Available versions (searched {len(available_versions)} across multiple pages): {', '.join(available_versions[:10])}..." 187 ) 188 sys.exit(1) 189 190 print(f"Found current version {current_version['version']} in GitHub releases") 191 192 if current_version == latest_version: 193 print(f"{PKG_NAME} is already up-to-date: {current_version['version']}") 194 return 195 196 print("Fetching release roadmap information...") 197 roadmap_url = "https://owncloud.dev/ocis/release_roadmap/" 198 try: 199 response = urllib.request.urlopen(roadmap_url) 200 content = response.read().decode("utf-8") 201 202 latest_version_channel = get_release_type(content, latest_version["version"]) 203 current_version_channel = get_release_type(content, current_version["version"]) 204 205 print( 206 f"Latest version {latest_version['version']} is in channel: {latest_version_channel}" 207 ) 208 print( 209 f"Current version {current_version['version']} is in channel: {current_version_channel}" 210 ) 211 except Exception as e: 212 print(f"Warning: Failed to fetch release roadmap information: {e}") 213 print("Proceeding with update using latest version") 214 latest_version_channel = TRACKING_CHANNEL 215 current_version_channel = TRACKING_CHANNEL 216 217 target_version = None 218 if latest_version_channel == TRACKING_CHANNEL: 219 target_version = latest_version 220 print( 221 f"Using latest version {latest_version['version']} as it is in the {TRACKING_CHANNEL} channel" 222 ) 223 elif latest_version_channel != TRACKING_CHANNEL: 224 print(f"Looking for a newer version in the {TRACKING_CHANNEL} channel...") 225 for version in all_versions: 226 try: 227 channel = get_release_type(content, version["version"]) 228 if ( 229 channel == TRACKING_CHANNEL 230 and version["published_date"] > current_version["published_date"] 231 ): 232 target_version = version 233 print( 234 f"{PKG_NAME} found newer version {version['version']} in channel {TRACKING_CHANNEL}" 235 ) 236 break 237 except Exception as e: 238 print( 239 f"Warning: Failed to determine channel for version {version['version']}: {e}" 240 ) 241 242 if not target_version: 243 print( 244 f"{PKG_NAME} could not find newer version in {TRACKING_CHANNEL} than the current {current_version['version']}" 245 ) 246 return 247 248 print( 249 f"Updating {PKG_NAME} from {current_version['version']} to {target_version['version']}" 250 ) 251 252 systems = [ 253 ("darwin", "arm64", "aarch64-darwin"), 254 ("darwin", "amd64", "x86_64-darwin"), 255 ("linux", "arm64", "aarch64-linux"), 256 ("linux", "arm", "armv7l-linux"), 257 ("linux", "amd64", "x86_64-linux"), 258 ("linux", "386", "i686-linux"), 259 ] 260 261 for os_name, arch, system in systems: 262 print(f"Calculating hash for {os_name}-{arch}...") 263 hash_value = get_hash(os_name, arch, target_version["version"]) 264 print(f"Updating package for {system}...") 265 update_source_version(PKG_NAME, target_version["version"], hash_value, system) 266 267 print(f"Successfully updated {PKG_NAME} to version {target_version['version']}") 268 269 270if __name__ == "__main__": 271 main()