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 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 def get_git_hash(self, rev: str): 53 return ( 54 subprocess.check_output( 55 [ 56 "nix-prefetch-url", 57 "--unpack", 58 f"https://gitlab.com/{self.owner}/{self.repo}/-/archive/{rev}/{self.repo}-{rev}.tar.gz", 59 ] 60 ) 61 .decode("utf-8") 62 .strip() 63 ) 64 65 def get_yarn_hash(self, rev: str): 66 with tempfile.TemporaryDirectory() as tmp_dir: 67 with open(tmp_dir + "/yarn.lock", "w") as f: 68 f.write(self.get_file("yarn.lock", rev)) 69 return ( 70 subprocess.check_output(["prefetch-yarn-deps", tmp_dir + "/yarn.lock"]) 71 .decode("utf-8") 72 .strip() 73 ) 74 75 @staticmethod 76 def rev2version(tag: str) -> str: 77 """ 78 normalize a tag to a version number. 79 This obviously isn't very smart if we don't pass something that looks like a tag 80 :param tag: the tag to normalize 81 :return: a normalized version number 82 """ 83 # strip v prefix 84 version = re.sub(r"^v", "", tag) 85 # strip -ee and -gitlab suffixes 86 return re.sub(r"-(ee|gitlab)$", "", version) 87 88 def get_file(self, filepath, rev): 89 """ 90 returns file contents at a given rev 91 :param filepath: the path to the file, relative to the repo root 92 :param rev: the rev to fetch at 93 :return: 94 """ 95 return requests.get(self.url + f"/raw/{rev}/{filepath}").text 96 97 def get_data(self, rev): 98 version = self.rev2version(rev) 99 100 passthru = { 101 v: self.get_file(v, rev).strip() 102 for v in [ 103 "GITALY_SERVER_VERSION", 104 "GITLAB_PAGES_VERSION", 105 "GITLAB_SHELL_VERSION", 106 "GITLAB_ELASTICSEARCH_INDEXER_VERSION", 107 ] 108 } 109 passthru["GITLAB_WORKHORSE_VERSION"] = version 110 111 return dict( 112 version=self.rev2version(rev), 113 repo_hash=self.get_git_hash(rev), 114 yarn_hash=self.get_yarn_hash(rev), 115 owner=self.owner, 116 repo=self.repo, 117 rev=rev, 118 passthru=passthru, 119 ) 120 121 122def _get_data_json(): 123 data_file_path = pathlib.Path(__file__).parent / "data.json" 124 with open(data_file_path, "r") as f: 125 return json.load(f) 126 127 128def _call_nix_update(pkg, version): 129 """calls nix-update from nixpkgs root dir""" 130 return subprocess.check_output( 131 ["nix-update", pkg, "--version", version], cwd=NIXPKGS_PATH 132 ) 133 134 135@click_log.simple_verbosity_option(logger) 136@click.group() 137def cli(): 138 pass 139 140 141@cli.command("update-data") 142@click.option("--rev", default="latest", help="The rev to use (vX.Y.Z-ee), or 'latest'") 143def update_data(rev: str): 144 """Update data.json""" 145 logger.info("Updating data.json") 146 147 repo = GitLabRepo() 148 if rev == "latest": 149 # filter out pre and rc releases 150 rev = next(filter(lambda x: not ("rc" in x or x.endswith("pre")), repo.tags)) 151 152 data_file_path = pathlib.Path(__file__).parent / "data.json" 153 154 data = repo.get_data(rev) 155 156 with open(data_file_path.as_posix(), "w") as f: 157 json.dump(data, f, indent=2) 158 f.write("\n") 159 160 161@cli.command("update-rubyenv") 162def update_rubyenv(): 163 """Update rubyEnv""" 164 logger.info("Updating gitlab") 165 repo = GitLabRepo() 166 rubyenv_dir = pathlib.Path(__file__).parent / "rubyEnv" 167 168 # load rev from data.json 169 data = _get_data_json() 170 rev = data["rev"] 171 version = data["version"] 172 173 for fn in ["Gemfile.lock", "Gemfile"]: 174 with open(rubyenv_dir / fn, "w") as f: 175 f.write(repo.get_file(fn, rev)) 176 177 # update to 1.2.9 to include https://gitlab.com/gitlab-org/ruby/gems/prometheus-client-mmap/-/commit/5d77f3f3e048834250589b416c6b3d4bba65a570 178 subprocess.check_output( 179 ["sed", "-i", "s:'prometheus-client-mmap', '~> 1.2.8':'prometheus-client-mmap', '~> 1.2.9':g", "Gemfile"], 180 cwd=rubyenv_dir, 181 ) 182 183 # Un-vendor sidekiq 184 # 185 # The sidekiq dependency was vendored to maintain compatibility with Redis 6.0 (as 186 # stated in this [comment]) but unfortunately, it seems to cause a crash in the 187 # application, as noted in this [upstream issue]. 188 # 189 # We can safely swap out the dependency, as our Redis release in nixpkgs is >= 7.0. 190 # 191 # [comment]: https://gitlab.com/gitlab-org/gitlab/-/issues/468435#note_1979750600 192 # [upstream issue]: https://gitlab.com/gitlab-org/gitlab/-/issues/468435 193 subprocess.check_output( 194 ["sed", "-i", "s|gem 'sidekiq', path: 'vendor/gems/sidekiq', require: 'sidekiq'|gem 'sidekiq', '~> 7.3.9'|g", "Gemfile"], 195 cwd=rubyenv_dir, 196 ) 197 198 # Fetch vendored dependencies temporarily in order to build the gemset.nix 199 subprocess.check_output(["mkdir", "-p", "vendor/gems", "gems"], cwd=rubyenv_dir) 200 subprocess.check_output( 201 [ 202 "sh", 203 "-c", 204 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", 205 ], 206 cwd=f"{rubyenv_dir}/vendor/gems", 207 ) 208 subprocess.check_output( 209 [ 210 "sh", 211 "-c", 212 f"curl -L https://gitlab.com/gitlab-org/gitlab/-/archive/v{version}-ee/gitlab-v{version}-ee.tar.bz2?path=gems | tar -xj --strip-components=2", 213 ], 214 cwd=f"{rubyenv_dir}/gems", 215 ) 216 217 # Undo our gemset.nix patches so that bundix runs through 218 subprocess.check_output( 219 ["sed", "-i", "-e", "s|\\${src}/||g", "gemset.nix"], cwd=rubyenv_dir 220 ) 221 subprocess.check_output( 222 ["sed", "-i", "-e", "s|^src:[[:space:]]||g", "gemset.nix"], cwd=rubyenv_dir 223 ) 224 225 subprocess.check_output(["bundle", "lock"], cwd=rubyenv_dir) 226 subprocess.check_output(["bundix"], cwd=rubyenv_dir) 227 228 subprocess.check_output( 229 [ 230 "sed", 231 "-i", 232 "-e", 233 "1c\\src: {", 234 "-e", 235 's:path = \\(vendor/[^;]*\\);:path = "${src}/\\1";:g', 236 "-e", 237 's:path = \\(gems/[^;]*\\);:path = "${src}/\\1";:g', 238 "gemset.nix", 239 ], 240 cwd=rubyenv_dir, 241 ) 242 subprocess.check_output(["rm", "-rf", "vendor", "gems"], cwd=rubyenv_dir) 243 244 # Reformat gemset.nix 245 subprocess.check_output(["nix-shell", "--run", "treefmt pkgs/by-name/gi/gitlab"], cwd=NIXPKGS_PATH) 246 247 248@cli.command("update-gitaly") 249def update_gitaly(): 250 """Update gitaly""" 251 logger.info("Updating gitaly") 252 data = _get_data_json() 253 gitaly_server_version = data['passthru']['GITALY_SERVER_VERSION'] 254 repo = GitLabRepo(repo="gitaly") 255 gitaly_dir = pathlib.Path(__file__).parent / 'gitaly' 256 257 makefile = repo.get_file("Makefile", f"v{gitaly_server_version}") 258 makefile += "\nprint-%:;@echo $($*)\n" 259 260 git_version = subprocess.run(["make", "-f", "-", "print-GIT_VERSION"], check=True, input=makefile, text=True, capture_output=True).stdout.strip() 261 262 _call_nix_update("gitaly", gitaly_server_version) 263 _call_nix_update("gitaly.git", git_version) 264 265 266@cli.command("update-gitlab-pages") 267def update_gitlab_pages(): 268 """Update gitlab-pages""" 269 logger.info("Updating gitlab-pages") 270 data = _get_data_json() 271 gitlab_pages_version = data["passthru"]["GITLAB_PAGES_VERSION"] 272 _call_nix_update("gitlab-pages", gitlab_pages_version) 273 274 275def get_container_registry_version() -> str: 276 """Returns the version attribute of gitlab-container-registry""" 277 return subprocess.check_output( 278 [ 279 "nix", 280 "--experimental-features", 281 "nix-command", 282 "eval", 283 "-f", 284 ".", 285 "--raw", 286 "gitlab-container-registry.version", 287 ], 288 cwd=NIXPKGS_PATH, 289 ).decode("utf-8") 290 291 292@cli.command("update-gitlab-shell") 293def update_gitlab_shell(): 294 """Update gitlab-shell""" 295 logger.info("Updating gitlab-shell") 296 data = _get_data_json() 297 gitlab_shell_version = data["passthru"]["GITLAB_SHELL_VERSION"] 298 _call_nix_update("gitlab-shell", gitlab_shell_version) 299 300 301@cli.command("update-gitlab-workhorse") 302def update_gitlab_workhorse(): 303 """Update gitlab-workhorse""" 304 logger.info("Updating gitlab-workhorse") 305 data = _get_data_json() 306 gitlab_workhorse_version = data["passthru"]["GITLAB_WORKHORSE_VERSION"] 307 _call_nix_update("gitlab-workhorse", gitlab_workhorse_version) 308 309 310@cli.command("update-gitlab-container-registry") 311@click.option("--rev", default="latest", help="The rev to use (vX.Y.Z-ee), or 'latest'") 312@click.option( 313 "--commit", is_flag=True, default=False, help="Commit the changes for you" 314) 315def update_gitlab_container_registry(rev: str, commit: bool): 316 """Update gitlab-container-registry""" 317 logger.info("Updading gitlab-container-registry") 318 repo = GitLabRepo(repo="container-registry") 319 old_container_registry_version = get_container_registry_version() 320 321 if rev == "latest": 322 rev = next(filter(lambda x: not ("rc" in x or x.endswith("pre")), repo.tags)) 323 324 version = repo.rev2version(rev) 325 _call_nix_update("gitlab-container-registry", version) 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 333@cli.command('update-gitlab-elasticsearch-indexer') 334def update_gitlab_elasticsearch_indexer(): 335 """Update gitlab-elasticsearch-indexer""" 336 data = _get_data_json() 337 gitlab_elasticsearch_indexer_version = data['passthru']['GITLAB_ELASTICSEARCH_INDEXER_VERSION'] 338 _call_nix_update('gitlab-elasticsearch-indexer', gitlab_elasticsearch_indexer_version) 339 340 341@cli.command("update-all") 342@click.option("--rev", default="latest", help="The rev to use (vX.Y.Z-ee), or 'latest'") 343@click.option( 344 "--commit", is_flag=True, default=False, help="Commit the changes for you" 345) 346@click.pass_context 347def update_all(ctx, rev: str, commit: bool): 348 """Update all gitlab components to the latest stable release""" 349 old_data_json = _get_data_json() 350 old_container_registry_version = get_container_registry_version() 351 352 ctx.invoke(update_data, rev=rev) 353 354 new_data_json = _get_data_json() 355 356 ctx.invoke(update_rubyenv) 357 ctx.invoke(update_gitaly) 358 ctx.invoke(update_gitlab_pages) 359 ctx.invoke(update_gitlab_shell) 360 ctx.invoke(update_gitlab_workhorse) 361 ctx.invoke(update_gitlab_elasticsearch_indexer) 362 if commit: 363 commit_gitlab( 364 old_data_json["version"], new_data_json["version"], new_data_json["rev"] 365 ) 366 367 ctx.invoke(update_gitlab_container_registry) 368 if commit: 369 new_container_registry_version = get_container_registry_version() 370 commit_container_registry( 371 old_container_registry_version, new_container_registry_version 372 ) 373 374 375def commit_gitlab(old_version: str, new_version: str, new_rev: str) -> None: 376 """Commits the gitlab changes for you""" 377 subprocess.run( 378 [ 379 "git", 380 "add", 381 "pkgs/by-name/gi/gitlab", 382 "pkgs/by-name/gi/gitaly", 383 "pkgs/by-name/gi/gitlab-elasticsearch-indexer", 384 "pkgs/by-name/gi/gitlab-pages", 385 ], 386 cwd=NIXPKGS_PATH, 387 ) 388 subprocess.run( 389 [ 390 "git", 391 "commit", 392 "--message", 393 f"""gitlab: {old_version} -> {new_version}\n\nhttps://gitlab.com/gitlab-org/gitlab/-/blob/{new_rev}/CHANGELOG.md""", 394 ], 395 cwd=NIXPKGS_PATH, 396 ) 397 398 399def commit_container_registry(old_version: str, new_version: str) -> None: 400 """Commits the gitlab-container-registry changes for you""" 401 subprocess.run( 402 [ 403 "git", 404 "add", 405 "pkgs/by-name/gi/gitlab-container-registry" 406 ], 407 cwd=NIXPKGS_PATH, 408 ) 409 subprocess.run( 410 [ 411 "git", 412 "commit", 413 "--message", 414 f"gitlab-container-registry: {old_version} -> {new_version}\n\nhttps://gitlab.com/gitlab-org/container-registry/-/blob/v{new_version}-gitlab/CHANGELOG.md", 415 ], 416 cwd=NIXPKGS_PATH, 417 ) 418 419 420if __name__ == "__main__": 421 cli()