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 bundix bundler nix-update nix nix-universal-prefetch python3 python3Packages.requests python3Packages.click python3Packages.click-log python3Packages.packaging prefetch-yarn-deps git 3 4import click 5import click_log 6import re 7import logging 8import subprocess 9import json 10import pathlib 11import tempfile 12from packaging.version import Version 13from typing import Iterable 14 15import requests 16 17NIXPKGS_PATH = pathlib.Path(__file__).parent / "../../../../" 18GITLAB_DIR = pathlib.Path(__file__).parent 19 20logger = logging.getLogger(__name__) 21click_log.basic_config(logger) 22 23 24class GitLabRepo: 25 version_regex = re.compile(r"^v\d+\.\d+\.\d+(\-rc\d+)?(\-ee)?(\-gitlab)?") 26 27 def __init__(self, owner: str = "gitlab-org", repo: str = "gitlab"): 28 self.owner = owner 29 self.repo = repo 30 31 @property 32 def url(self): 33 return f"https://gitlab.com/{self.owner}/{self.repo}" 34 35 @property 36 def tags(self) -> Iterable[str]: 37 """Returns a sorted list of repository tags""" 38 r = requests.get(self.url + "/refs?sort=updated_desc&ref=master").json() 39 tags = r.get("Tags", []) 40 41 # filter out versions not matching version_regex 42 versions = list(filter(self.version_regex.match, tags)) 43 44 # sort, but ignore v, -ee and -gitlab for sorting comparisons 45 versions.sort( 46 key=lambda x: Version( 47 x.replace("v", "").replace("-ee", "").replace("-gitlab", "") 48 ), 49 reverse=True, 50 ) 51 return versions 52 53 def get_git_hash(self, rev: str): 54 return ( 55 subprocess.check_output( 56 [ 57 "nix-universal-prefetch", 58 "fetchFromGitLab", 59 "--owner", 60 self.owner, 61 "--repo", 62 self.repo, 63 "--rev", 64 rev, 65 ] 66 ) 67 .decode("utf-8") 68 .strip() 69 ) 70 71 def get_yarn_hash(self, rev: str): 72 with tempfile.TemporaryDirectory() as tmp_dir: 73 with open(tmp_dir + "/yarn.lock", "w") as f: 74 f.write(self.get_file("yarn.lock", rev)) 75 return ( 76 subprocess.check_output(["prefetch-yarn-deps", tmp_dir + "/yarn.lock"]) 77 .decode("utf-8") 78 .strip() 79 ) 80 81 @staticmethod 82 def rev2version(tag: str) -> str: 83 """ 84 normalize a tag to a version number. 85 This obviously isn't very smart if we don't pass something that looks like a tag 86 :param tag: the tag to normalize 87 :return: a normalized version number 88 """ 89 # strip v prefix 90 version = re.sub(r"^v", "", tag) 91 # strip -ee and -gitlab suffixes 92 return re.sub(r"-(ee|gitlab)$", "", version) 93 94 def get_file(self, filepath, rev): 95 """ 96 returns file contents at a given rev 97 :param filepath: the path to the file, relative to the repo root 98 :param rev: the rev to fetch at 99 :return: 100 """ 101 return requests.get(self.url + f"/raw/{rev}/{filepath}").text 102 103 def get_data(self, rev): 104 version = self.rev2version(rev) 105 106 passthru = { 107 v: self.get_file(v, rev).strip() 108 for v in [ 109 "GITALY_SERVER_VERSION", 110 "GITLAB_PAGES_VERSION", 111 "GITLAB_SHELL_VERSION", 112 ] 113 } 114 115 passthru["GITLAB_WORKHORSE_VERSION"] = version 116 117 return dict( 118 version=self.rev2version(rev), 119 repo_hash=self.get_git_hash(rev), 120 yarn_hash=self.get_yarn_hash(rev), 121 owner=self.owner, 122 repo=self.repo, 123 rev=rev, 124 passthru=passthru, 125 ) 126 127 128def _get_data_json(): 129 data_file_path = pathlib.Path(__file__).parent / "data.json" 130 with open(data_file_path, "r") as f: 131 return json.load(f) 132 133 134def _call_nix_update(pkg, version): 135 """calls nix-update from nixpkgs root dir""" 136 return subprocess.check_output( 137 ["nix-update", pkg, "--version", version], cwd=NIXPKGS_PATH 138 ) 139 140 141@click_log.simple_verbosity_option(logger) 142@click.group() 143def cli(): 144 pass 145 146 147@cli.command("update-data") 148@click.option("--rev", default="latest", help="The rev to use (vX.Y.Z-ee), or 'latest'") 149def update_data(rev: str): 150 """Update data.json""" 151 logger.info("Updating data.json") 152 153 repo = GitLabRepo() 154 if rev == "latest": 155 # filter out pre and rc releases 156 rev = next(filter(lambda x: not ("rc" in x or x.endswith("pre")), repo.tags)) 157 158 data_file_path = pathlib.Path(__file__).parent / "data.json" 159 160 data = repo.get_data(rev) 161 162 with open(data_file_path.as_posix(), "w") as f: 163 json.dump(data, f, indent=2) 164 f.write("\n") 165 166 167@cli.command("update-rubyenv") 168def update_rubyenv(): 169 """Update rubyEnv""" 170 logger.info("Updating gitlab") 171 repo = GitLabRepo() 172 rubyenv_dir = pathlib.Path(__file__).parent / "rubyEnv" 173 174 # load rev from data.json 175 data = _get_data_json() 176 rev = data["rev"] 177 version = data["version"] 178 179 for fn in ["Gemfile.lock", "Gemfile"]: 180 with open(rubyenv_dir / fn, "w") as f: 181 f.write(repo.get_file(fn, rev)) 182 183 # patch for openssl 3.x support 184 subprocess.check_output( 185 ["sed", "-i", "s:'openssl', '2.*':'openssl', '3.0.2':g", "Gemfile"], 186 cwd=rubyenv_dir, 187 ) 188 189 # Fetch vendored dependencies temporarily in order to build the gemset.nix 190 subprocess.check_output(["mkdir", "-p", "vendor/gems"], cwd=rubyenv_dir) 191 subprocess.check_output( 192 [ 193 "sh", 194 "-c", 195 f"curl -L https://gitlab.com/gitlab-org/gitlab/-/archive/v{version}-ee/gitlab-v{version}-ee.tar.bz2?path=vendor/gems | tar -xj --strip-components=3", 196 ], 197 cwd=f"{rubyenv_dir}/vendor/gems", 198 ) 199 200 # Undo our gemset.nix patches so that bundix runs through 201 subprocess.check_output( 202 ["sed", "-i", "-e", "1d", "-e", "s:\\${src}/::g", "gemset.nix"], cwd=rubyenv_dir 203 ) 204 205 subprocess.check_output(["bundle", "lock"], cwd=rubyenv_dir) 206 subprocess.check_output(["bundix"], cwd=rubyenv_dir) 207 208 subprocess.check_output( 209 [ 210 "sed", 211 "-i", 212 "-e", 213 "1i\\src:", 214 "-e", 215 's:path = \\(vendor/[^;]*\\);:path = "${src}/\\1";:g', 216 "gemset.nix", 217 ], 218 cwd=rubyenv_dir, 219 ) 220 subprocess.check_output(["rm", "-rf", "vendor"], cwd=rubyenv_dir) 221 222 223@cli.command("update-gitaly") 224def update_gitaly(): 225 """Update gitaly""" 226 logger.info("Updating gitaly") 227 data = _get_data_json() 228 gitaly_server_version = data['passthru']['GITALY_SERVER_VERSION'] 229 230 _call_nix_update("gitaly", gitaly_server_version) 231 232 233@cli.command("update-gitlab-pages") 234def update_gitlab_pages(): 235 """Update gitlab-pages""" 236 logger.info("Updating gitlab-pages") 237 data = _get_data_json() 238 gitlab_pages_version = data["passthru"]["GITLAB_PAGES_VERSION"] 239 _call_nix_update("gitlab-pages", gitlab_pages_version) 240 241 242def get_container_registry_version() -> str: 243 """Returns the version attribute of gitlab-container-registry""" 244 return subprocess.check_output( 245 [ 246 "nix", 247 "--experimental-features", 248 "nix-command", 249 "eval", 250 "-f", 251 ".", 252 "--raw", 253 "gitlab-container-registry.version", 254 ], 255 cwd=NIXPKGS_PATH, 256 ).decode("utf-8") 257 258 259@cli.command("update-gitlab-shell") 260def update_gitlab_shell(): 261 """Update gitlab-shell""" 262 logger.info("Updating gitlab-shell") 263 data = _get_data_json() 264 gitlab_shell_version = data["passthru"]["GITLAB_SHELL_VERSION"] 265 _call_nix_update("gitlab-shell", gitlab_shell_version) 266 267 268@cli.command("update-gitlab-workhorse") 269def update_gitlab_workhorse(): 270 """Update gitlab-workhorse""" 271 logger.info("Updating gitlab-workhorse") 272 data = _get_data_json() 273 gitlab_workhorse_version = data["passthru"]["GITLAB_WORKHORSE_VERSION"] 274 _call_nix_update("gitlab-workhorse", gitlab_workhorse_version) 275 276 277@cli.command("update-gitlab-container-registry") 278@click.option("--rev", default="latest", help="The rev to use (vX.Y.Z-ee), or 'latest'") 279@click.option( 280 "--commit", is_flag=True, default=False, help="Commit the changes for you" 281) 282def update_gitlab_container_registry(rev: str, commit: bool): 283 """Update gitlab-container-registry""" 284 logger.info("Updading gitlab-container-registry") 285 repo = GitLabRepo(repo="container-registry") 286 old_container_registry_version = get_container_registry_version() 287 288 if rev == "latest": 289 rev = next(filter(lambda x: not ("rc" in x or x.endswith("pre")), repo.tags)) 290 291 version = repo.rev2version(rev) 292 _call_nix_update("gitlab-container-registry", version) 293 if commit: 294 new_container_registry_version = get_container_registry_version() 295 commit_container_registry( 296 old_container_registry_version, new_container_registry_version 297 ) 298 299 300@cli.command("update-all") 301@click.option("--rev", default="latest", help="The rev to use (vX.Y.Z-ee), or 'latest'") 302@click.option( 303 "--commit", is_flag=True, default=False, help="Commit the changes for you" 304) 305@click.pass_context 306def update_all(ctx, rev: str, commit: bool): 307 """Update all gitlab components to the latest stable release""" 308 old_data_json = _get_data_json() 309 old_container_registry_version = get_container_registry_version() 310 311 ctx.invoke(update_data, rev=rev) 312 313 new_data_json = _get_data_json() 314 315 ctx.invoke(update_rubyenv) 316 ctx.invoke(update_gitaly) 317 ctx.invoke(update_gitlab_pages) 318 ctx.invoke(update_gitlab_shell) 319 ctx.invoke(update_gitlab_workhorse) 320 if commit: 321 commit_gitlab( 322 old_data_json["version"], new_data_json["version"], new_data_json["rev"] 323 ) 324 325 ctx.invoke(update_gitlab_container_registry) 326 if commit: 327 new_container_registry_version = get_container_registry_version() 328 commit_container_registry( 329 old_container_registry_version, new_container_registry_version 330 ) 331 332 333def commit_gitlab(old_version: str, new_version: str, new_rev: str) -> None: 334 """Commits the gitlab changes for you""" 335 subprocess.run( 336 [ 337 "git", 338 "add", 339 "data.json", 340 "rubyEnv", 341 "gitaly", 342 "gitlab-pages", 343 "gitlab-shell", 344 "gitlab-workhorse", 345 ], 346 cwd=GITLAB_DIR, 347 ) 348 subprocess.run( 349 [ 350 "git", 351 "commit", 352 "--message", 353 f"""gitlab: {old_version} -> {new_version}\n\nhttps://gitlab.com/gitlab-org/gitlab/-/blob/{new_rev}/CHANGELOG.md""", 354 ], 355 cwd=GITLAB_DIR, 356 ) 357 358 359def commit_container_registry(old_version: str, new_version: str) -> None: 360 """Commits the gitlab-container-registry changes for you""" 361 subprocess.run(["git", "add", "gitlab-container-registry"], cwd=GITLAB_DIR) 362 subprocess.run( 363 [ 364 "git", 365 "commit", 366 "--message", 367 f"gitlab-container-registry: {old_version} -> {new_version}", 368 ], 369 cwd=GITLAB_DIR, 370 ) 371 372 373if __name__ == "__main__": 374 cli()