in abandon_stale.py [0:0]
def _main():
parser = optparse.OptionParser()
parser.add_option(
"-g",
"--gerrit-url",
dest="gerrit_url",
metavar="URL",
default=None,
help="gerrit server URL",
)
parser.add_option(
"-b",
"--basic-auth",
dest="basic_auth",
action="store_true",
help="(deprecated) use HTTP basic authentication instead of digest",
)
parser.add_option(
"-d",
"--digest-auth",
dest="digest_auth",
action="store_true",
help="use HTTP digest authentication instead of basic",
)
parser.add_option(
"-n",
"--dry-run",
dest="dry_run",
action="store_true",
help="enable dry-run mode: show stale changes but do not abandon them",
)
parser.add_option(
"-t",
"--test",
dest="testmode",
action="store_true",
help="test mode: query changes with the `test-abandon` "
"topic and ignore age option",
)
parser.add_option(
"-a",
"--age",
dest="age",
metavar="AGE",
default="6months",
help="age of change since last update in days, months"
" or years (default: %default)",
)
parser.add_option(
"-m",
"--message",
dest="message",
metavar="STRING",
default=None,
help="custom message to append to abandon message",
)
parser.add_option(
"--branch",
dest="branches",
metavar="BRANCH_NAME",
default=[],
action="append",
help="abandon changes only on the given branch",
)
parser.add_option(
"--exclude-branch",
dest="exclude_branches",
metavar="BRANCH_NAME",
default=[],
action="append",
help="do not abandon changes on given branch",
)
parser.add_option(
"--project",
dest="projects",
metavar="PROJECT_NAME",
default=[],
action="append",
help="abandon changes only on the given project",
)
parser.add_option(
"--exclude-project",
dest="exclude_projects",
metavar="PROJECT_NAME",
default=[],
action="append",
help="do not abandon changes on given project",
)
parser.add_option(
"--owner",
dest="owner",
metavar="USERNAME",
default=None,
action="store",
help="only abandon changes owned by the given user",
)
parser.add_option(
"--exclude-wip",
dest="exclude_wip",
action="store_true",
help="Exclude changes that are Work-in-Progress",
)
parser.add_option(
"-v",
"--verbose",
dest="verbose",
action="store_true",
help="enable verbose (debug) logging",
)
(options, _args) = parser.parse_args()
level = logging.DEBUG if options.verbose else logging.INFO
logging.basicConfig(format="%(asctime)s %(levelname)s %(message)s", level=level)
if not options.gerrit_url:
logging.error("Gerrit URL is required")
return 1
if options.testmode:
message = "Abandoning in test mode"
else:
pattern = re.compile(r"^([\d]+)(month[s]?|year[s]?|week[s]?)")
match = pattern.match(options.age)
if not match:
logging.error("Invalid age: %s", options.age)
return 1
message = "Abandoning after %s %s or more of inactivity." % (
match.group(1),
match.group(2),
)
if options.digest_auth:
auth_type = HTTPDigestAuthFromNetrc
else:
auth_type = HTTPBasicAuthFromNetrc
try:
auth = auth_type(url=options.gerrit_url)
gerrit = GerritRestAPI(url=options.gerrit_url, auth=auth)
except Exception as e:
logging.error(e)
return 1
logging.info(message)
try:
stale_changes = []
offset = 0
step = 500
if options.testmode:
query_terms = ["status:new", "owner:self", "topic:test-abandon"]
else:
query_terms = ["status:new", "age:%s" % options.age]
if options.exclude_wip:
query_terms += ["-is:wip"]
if options.branches:
query_terms += ["branch:%s" % b for b in options.branches]
elif options.exclude_branches:
query_terms += ["-branch:%s" % b for b in options.exclude_branches]
if options.projects:
query_terms += ["project:%s" % p for p in options.projects]
elif options.exclude_projects:
query_terms = ["-project:%s" % p for p in options.exclude_projects]
if options.owner and not options.testmode:
query_terms += ["owner:%s" % options.owner]
query = "%20".join(query_terms)
while True:
q = query + "&o=DETAILED_ACCOUNTS&n=%d&S=%d" % (step, offset)
logging.debug("Query: %s", q)
url = "/changes/?q=" + q
result = gerrit.get(url)
logging.debug("%d changes", len(result))
if not result:
break
stale_changes += result
last = result[-1]
if "_more_changes" in last:
logging.debug("More...")
offset += step
else:
break
except Exception as e:
logging.error(e)
return 1
abandoned = 0
errors = 0
abandon_message = message
if options.message:
abandon_message += "\n\n" + options.message
for change in stale_changes:
number = change["_number"]
project = ""
if len(options.projects) != 1:
project = "%s: " % change["project"]
owner = ""
if options.verbose:
try:
o = change["owner"]["name"]
except KeyError:
o = "Unknown"
owner = " (%s)" % o
subject = change["subject"]
if len(subject) > 70:
subject = subject[:65] + " [...]"
change_id = change["id"]
logging.info("%s%s: %s%s", number, owner, project, subject)
if options.dry_run:
continue
try:
gerrit.post(
"/changes/" + change_id + "/abandon",
json={"message": "%s" % abandon_message},
)
abandoned += 1
except Exception as e:
errors += 1
logging.error(e)
logging.info("Total %d stale open changes", len(stale_changes))
if not options.dry_run:
logging.info("Abandoned %d changes. %d errors.", abandoned, errors)