# Copyright (c) 2025 The Brave Authors. All rights reserved. # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this file, # You can obtain one at https://mozilla.org/MPL/2.0/. """ This script bumps Nala to a new version (or the latest version on the main branch, if none is provided). It commits the bumped version and lists all the changes in the commit message, and links to the commits in Nala, to make it obvious what's new. """ import argparse import sys import os import subprocess import tempfile import json import re REPO_ID = 'brave/leo' NALA_REPO = f'https://github.com/{REPO_ID}' nala_clone_dir = tempfile.mkdtemp() SF_SYMBOLS_REPO_ID = 'brave/leo-sf-symbols' SF_SYMBOLS_REPO = f'https://github.com/{SF_SYMBOLS_REPO_ID}' sf_symbols_clone_dir = tempfile.mkdtemp() class Commit: """ Represents a commit in a repository. This also handles updating links based on Github PR numbers to a full link so they work from the brave-core repo. """ def __init__(self, log_line, repo_url=NALA_REPO): parts = log_line.split(' ', 1) self.sha = parts[0] self.message = parts[1] self.repo_url = repo_url def __str__(self): # Update the PR links to point to the repo return re.sub(r'\((#(\d+))\)$', rf'([\1]({self.repo_url}/pull/\2))', self.message) def __repr__(self): return self.__str__() def parse_args(): parser = argparse.ArgumentParser(description='Bumps Nala to a new version') parser.add_argument( '--target', help='The version to bump to. Can be a commit, tag or branch.', default="main") parser.add_argument('--push', help='Push the changes to the remote repository', action='store_true', default=False) return parser.parse_args() def clone_repo(url, clone_dir): """Clones a repository to the given directory.""" subprocess.run( ['git', 'clone', url + '.git', clone_dir, '--filter', 'blob:none'], check=True) def get_full_sha(target, clone_dir): """Gets the full sha of a commit, tag or branch in the given clone.""" cwd = os.getcwd() try: os.chdir(clone_dir) return subprocess.run( ['git', 'rev-parse', target], check=True, capture_output=True).stdout.decode('utf-8').strip() finally: os.chdir(cwd) def get_nala_current_sha(): """Gets the current sha of Nala from the package.json file.""" with open('package.json', 'r') as f: package_json = json.load(f) return package_json['dependencies']['@brave/leo'].split('#')[1] def get_sf_symbols_current_sha(): """Gets the current sha of leo-sf-symbols from the package.json file.""" with open('package.json', 'r') as f: package_json = json.load(f) return package_json['dependencies']['@brave/leo-sf-symbols'].split('#')[1] def get_changes(old_sha, new_sha, clone_dir, repo_url=NALA_REPO): """ Gets the commits between two shas in a repository. Returns a list of Commit objects. """ cwd = os.getcwd() try: os.chdir(clone_dir) result = subprocess.run([ 'git', 'log', '--oneline', '--no-decorate', f'{old_sha}...{new_sha}' ], check=True, capture_output=True) commits = result.stdout.decode('utf-8').split('\n') return [Commit(commit, repo_url) for commit in commits if commit] finally: os.chdir(cwd) def bump_nala(target): """ Bumps Nala to a new version and commits the changes (with a commit message listing all the changes that landed in Nala since the last bump). Also installs the latest leo-sf-symbols commit. """ old_sha = get_nala_current_sha() clone_repo(NALA_REPO, nala_clone_dir) clone_repo(SF_SYMBOLS_REPO, sf_symbols_clone_dir) new_sha = get_full_sha(target, nala_clone_dir) new_sf_symbols_sha = get_full_sha('main', sf_symbols_clone_dir) if old_sha == new_sha: print(f"Nala is already at {target}. Nothing to do.") return changes = get_changes(old_sha, new_sha, nala_clone_dir) changes_text = "\n".join(map(lambda x: f'- {x}', changes)) commit_message = f"""[Nala]: Bump version to {target} Changes {NALA_REPO}/compare/{old_sha}...{new_sha} {changes_text}""" subprocess.run([ 'npm', 'install', f'github:{REPO_ID}#{new_sha}', f'github:{SF_SYMBOLS_REPO_ID}#{new_sf_symbols_sha}' ], check=True) subprocess.run([ 'git', 'commit', 'package.json', 'package-lock.json', '-m', commit_message ], check=True) def main(): args = parse_args() bump_nala(args.target) if args.push: subprocess.run(['git', 'push'], check=True) if __name__ == '__main__': main()