| #!/usr/bin/env python3 |
| # |
| # Copyright (c) 2019-2020 Arm Limited. All rights reserved. |
| # |
| # SPDX-License-Identifier: BSD-3-Clause |
| # |
| |
| # This scripts translates certain accepted refspec schemes to something that can |
| # be used on git command line. For example, given the refspec 'topic:foo/bar' |
| # for a given project, this script translates and prints the full commit hash. |
| # |
| # If a scheme is not recognized, print the received refspec unchanged. |
| |
| import argparse |
| import gerrit |
| import sys |
| |
| # Gerrit servers we care about. |
| gerrit_arm = gerrit.GerritServer("gerrit.oss.arm.com") |
| gerrit_tforg = gerrit.GerritServer("review.trustedfirmware.org") |
| |
| # Trusted Firmware-A and associated projects. |
| # Different projects are hosted on different Gerrit servers. |
| projects = { |
| # Projects hosted on Arm Gerrit server. |
| "arm": { |
| "trusted-firmware": gerrit.GerritProject("pdcs-platforms/ap/tf-topics", gerrit_arm), |
| "trusted-firmware-tf": gerrit.GerritProject("trusted-firmware/tf-a-tests", gerrit_arm), |
| "trusted-firmware-ci": gerrit.GerritProject("pdswinf/ci/pdcs-platforms/platform-ci", gerrit_arm), |
| "cc_plugin": gerrit.GerritProject("tests/lava/test-definitions.git", gerrit_arm), |
| "scp": gerrit.GerritProject("scp/firmware", gerrit_arm), |
| "spm": gerrit.GerritProject("trusted-firmware/spm", gerrit_arm), |
| }, |
| |
| # Projects hosted on trustedfirmware.org Gerrit server. |
| "tforg": { |
| "trusted-firmware": gerrit.GerritProject("TF-A/trusted-firmware-a", gerrit_tforg), |
| "trusted-firmware-tf": gerrit.GerritProject("TF-A/tf-a-tests", gerrit_tforg), |
| "trusted-firmware-ci": gerrit.GerritProject("ci/tf-a-ci-scripts", gerrit_tforg), |
| "spm": gerrit.GerritProject("hafnium/hafnium", gerrit_tforg), |
| }, |
| } |
| |
| # Argument setup |
| parser = argparse.ArgumentParser() |
| parser.add_argument("--project", "-p", |
| help="Gerrit project identifier this refspec belongs to") |
| parser.add_argument("--server", "-s", help="Gerrit server hosting this project", |
| choices=["arm", "tforg"]) |
| parser.add_argument("--user", "-u", |
| help="Username to use to query the Gerrit server") |
| parser.add_argument("--key", "-k", |
| help="SSH private key to use to authenticate with the Gerrit server") |
| parser.add_argument("refspec", help="Refspec to translate") |
| opts = parser.parse_args() |
| |
| project = projects[opts.server][opts.project] |
| |
| # Default action: print refspec and exit |
| def do_default(): |
| print(opts.refspec) |
| sys.exit(0) |
| |
| def print_topic_tip(query_results): |
| patchsets = [] |
| parents = [] |
| |
| # For each change, get its most recent patchset |
| for change in query_results: |
| patchsets.append(change["patchSets"][-1]) |
| |
| # For each patchset, get its parent commit |
| for patchset in patchsets: |
| parents.append(patchset["parents"][0]) |
| |
| # If a patchset's revision is NOT in the list of parents then it should |
| # be the tip commit |
| tips = list(filter(lambda x: x["revision"] not in parents, patchsets)) |
| |
| # There must be only one patchset remaining, otherwise the tip is ambiguous |
| if len(tips) > 1: |
| raise Exception("{} in {} has no unique tip commit.".format(opts.refspec, |
| opts.project)) |
| if len(tips) == 0: |
| raise Exception("No tip commit found for {} in {}.".format(opts.refspec, |
| opts.project)) |
| # Print the reference of the topic tip patchset |
| print(tips[0]["ref"]) |
| |
| query = ["status:open"] |
| |
| # If we don't understand the refspec, that's OK. We don't translate it, but |
| # print it as is. |
| try: |
| scheme, rest = opts.refspec.split(":") |
| if scheme == "topic": |
| query += ["topic:" + rest] |
| elif scheme == "change": |
| query += [opts.refspec] |
| else: |
| do_default() |
| except: |
| do_default() |
| |
| changes = project.query(query, username=opts.user, keyfile=opts.key) |
| |
| # The last object is a summary; drop it as it's not of interest to us. |
| changes.pop() |
| |
| if not changes: |
| raise Exception("{} for {} resolved to nothing.".format(opts.refspec, |
| opts.project)) |
| |
| if scheme == "topic": |
| if len(changes) > 1: |
| print_topic_tip(changes) |
| else: |
| print(changes[0]["currentPatchSet"]["ref"]) |
| elif scheme == "change": |
| if len(changes) > 1: |
| # When querying for a specific change there must be just a single result |
| raise Exception("{} for {} did not resolve uniquely.".format(opts.refspec, |
| opts.project)) |
| print(changes[0]["currentPatchSet"]["revision"]) |