#!/bin/env python3 import argparse import json import os import shutil import sys import urllib.request import subprocess from portage.dbapi.porttree import portdbapi from portage.versions import * from portage.package.ebuild import digestgen, config from portage.output import EOutput from git import Repo channels = ["stable", "beta", "dev"] pkg_data = \ { "www-client" : { "stable" : { "pkg" : "google-chrome", "suffix" : None, "version" : None, "bump" : False, "stable" : True }, "beta" : { "pkg" : "google-chrome-beta", "suffix" : None, "version" : None, "bump" : False, "stable" : False }, "dev" : { "pkg" : "google-chrome-unstable", "suffix" : None, "version" : None, "bump" : False, "stable" : False } }, "www-plugins" : { "stable" : { "pkg" : "chrome-binary-plugins", "suffix" : None, "version" : None, "bump" : False, "stable" : True }, "beta" : { "pkg" : "chrome-binary-plugins", "suffix" : "beta", "version" : None, "bump" : False, "stable" : False }, "dev" : { "pkg" : "chrome-binary-plugins", "suffix" : "alpha", "version" : None, "bump" : False, "stable" : False } }, # This will be parsed last so we can take advantage of google chrome stable queries "www-apps": { "stable": { "pkg": "chromedriver-bin", "suffix": None, "version": None, "bump": False, "stable": True } }, } def getChromeVersionData(base_url, os, channel): if not base_url.endswith("/"): url = base_url + "/" url += f"{os}/channels/{channel}/versions/all/releases?filter=endtime=1970-01-01T00:00:00Z" response = urllib.request.urlopen(url) data = json.loads(response.read()) return data["releases"][0]["version"] def isMajorBump(uversion, tversion): uv_list = uversion.split('.') tv_list = tversion.split('.') if int(uv_list[0]) > int(tv_list[0]): return True return False def getPrevChannel(channel): channel_list = channels + [channels[len(channels) - 1]] for i in range(0, len(channel_list) - 1): if channel_list[i] == channel: return channel_list[i + 1] raise ValueError(f"Unknown channel \"{channel}\".") def getEbuildVersion(version): if version[1] == "r0": return version[0] return f"{version[0]}-{version[1]}" def main(): parser = argparse.ArgumentParser() parser.add_argument('--dry-run', '-n', action='store_true') args = parser.parse_args() output = EOutput() output.einfo("Fetching upstream version information ...") chrome_info = {} for channel in channels: chrome_info[channel] = None for channel in channels: version = getChromeVersionData(base_url="https://versionhistory.googleapis.com/v1/chrome/platforms", os="linux", channel=channel) chrome_info[channel] = version output.einfo("Looking up Chrome version information in tree ...") db = portdbapi() repo_path = db.getRepositoryPath(repository_id="gentoo") for category in pkg_data.keys(): for channel in channels: # We only care about chromedriver that matches the stable version of google-chrome if category == "www-apps" and channel != "stable": continue pkg = pkg_data[category][channel]["pkg"] cpvs = db.cp_list(mycp=f"{category}/{pkg}", mytree=repo_path) pkg_data[category][channel]["version"] = None for cpv in cpvs: (cp, version, rev) = pkgsplit(mypkg=cpv) suffix = pkg_data[category][channel]["suffix"] if suffix is not None: suffix = "_" + suffix if version.endswith(suffix): pkg_data[category][channel]["version"] = (version[:-len(suffix)], rev) elif not "_" in version: pkg_data[category][channel]["version"] = (version, rev) if pkg_data[category][channel]["version"] is None: output.ewarn("Couldn't determine tree version for "+ "{category}/{pkg}") output.einfo("Comparing Chrome version information...") for channel in channels: if chrome_info[channel] is None: output.ewarn(f"Upstream version unknown for channel \"{channel}\".") else: for category in pkg_data.keys(): # chromedriver-bin is basically a shim for google-chrome stable for version purposes if category == "www-apps": if channel == "stable": pkg_data[category][channel]["bump"] = pkg_data["www-client"][channel]["bump"] pkg_data[category][channel]["version"] = pkg_data["www-client"][channel]["version"] else: continue else: pkg_data[category][channel]["bump"] = False ver_info = vercmp(chrome_info[channel], pkg_data[category][channel]["version"][0]) if ver_info is None: output.ewarn("Cannot determine new version for " + f"channel \"{channel}\" of " + f"{category}/" + f"{pkg_data[category][channel]['pkg']}.") elif ver_info > 0: pkg_data[category][channel]["bump"] = True elif ver_info < 0: output.ewarn("Upstream reverted bump for " + f"channel \"{channel}\" of " + f"{category}/" + f"{pkg_data[category][channel]['pkg']}.") for category in pkg_data.keys(): for channel in channels: if category == "www-apps" and channel != "stable": continue pkg = pkg_data[category][channel]["pkg"] output.einfo(f"{category}/{pkg} version information:") need_bump = pkg_data[category][channel]["bump"] uversion = chrome_info[channel] tversion = getEbuildVersion(pkg_data[category][channel]["version"]) output.einfo(f"\t{channel}\t{tversion}\t{uversion}" + f"\t==> {'bump' if need_bump else 'no bump'}") if not args.dry_run: repo = Repo(repo_path) if repo.is_dirty(): output.eerror("Git Repository is dirty, can't continue.") sys.exit(1) index = repo.index for channel in channels: for category in pkg_data.keys(): if category == "www-apps" and channel != "stable": continue if not pkg_data[category][channel]["bump"]: continue uversion = chrome_info[channel] tversion = getEbuildVersion(pkg_data[category][channel]["version"]) major_bump = isMajorBump(uversion=uversion, tversion=pkg_data[category][channel]["version"][0]) pkg = pkg_data[category][channel]["pkg"] suffix = pkg_data[category][channel]["suffix"] if suffix is not None: suffix = "_" + suffix else: suffix = "" output.einfo(f"Bumping {category}/{pkg} ...") if major_bump: if category != "www-apps": prev_channel = getPrevChannel(channel=channel) prev_pkg = pkg_data[category][prev_channel]["pkg"] prev_version = getEbuildVersion(pkg_data[category][prev_channel]["version"]) prev_suffix = pkg_data[category][prev_channel]["suffix"] else: # Grab the details for google-chrome; we never have a suffix prev_pkg = pkg_data["www-apps"]["stable"]["pkg"] prev_version = getEbuildVersion(pkg_data["www-client"]["stable"]["version"]) prev_suffix = None if prev_suffix is not None: prev_suffix = "_" + prev_suffix else: prev_suffix = "" from_ebuild = os.path.join(category, prev_pkg, prev_pkg + "-" + prev_version + prev_suffix + ".ebuild") else: from_ebuild = os.path.join(category, pkg, pkg + "-" + tversion + suffix + ".ebuild") to_ebuild = os.path.join(category, pkg, pkg + "-" + uversion + suffix + ".ebuild") if args.dry_run: print(f"cp {from_ebuild} {to_ebuild}") if not major_bump: print(f"git rm {from_ebuild}") else: from_ebuild = os.path.join(repo_path, from_ebuild) shutil.copyfile(from_ebuild, os.path.join(repo_path, to_ebuild)) if not major_bump: index.remove(from_ebuild, working_tree=True) if major_bump: old_ebuild = os.path.join(category, pkg, pkg + "-" + tversion + suffix + ".ebuild") if args.dry_run: print(f"git rm {old_ebuild}") else: index.remove(os.path.join(repo_path, old_ebuild), working_tree=True) if pkg_data[category][channel]["stable"]: if args.dry_run: print(f"ekeyword amd64 {to_ebuild}") else: subprocess.run(["ekeyword", "amd64", os.path.join(repo_path, to_ebuild)]) if args.dry_run: print(f"git add {to_ebuild}") else: to_ebuild = os.path.join(repo_path, to_ebuild) index.add(to_ebuild) to_path = os.path.dirname(to_ebuild) cfg = config.config() cfg["O"] = to_path if args.dry_run: print(f"git add {os.path.join(to_path, 'Manifest')}") print("git commit -m", f"\"{category}/{pkg}: automated update", f"({uversion}{suffix})", "-s -S\"") else: digestgen.digestgen(None, cfg, db) index.add(os.path.join(to_path, "Manifest")) repo.git.commit("-m", f"{category}/{pkg}: automated update ({uversion}{suffix})", "-s", "-S") if __name__ == "__main__": main()