#!/usr/bin/env python3 # # Copyright 2024 Acme Gating, LLC # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import sys import json import requests import argparse BREAK = '------------------------------------------------------------' class Gerrit: def __init__(self, url, username, password): if url.endswith('/'): url = url[:-1] self.url = url self.session = requests.Session() self.session.auth = requests.auth.HTTPBasicAuth(username, password) def get(self, path): url = f'{self.url}{path}' r = self.session.get(url, headers={'Accept': 'application/json', 'Accept-Encoding': 'gzip'}) if r.status_code == 200: ret = json.loads(r.text[4:]) return ret def post(self, path, data): url = f'{self.url}{path}' r = self.session.post( url, data=json.dumps(data).encode('utf8'), headers={'Content-Type': 'application/json;charset=UTF-8'}) if r.status_code > 400: raise Exception("POST to %s failed with http code %s (%s)", path, r.status_code, r.text) if r.text and len(r.text) > 4: return json.loads(r.text[4:]) def list_messages(self, change): data = self.get(f'/a/changes/{change}/messages') for msg in data: name = msg['author'].get('name') username = msg['author'].get('username') email = msg['author'].get('email') date = msg['date'] msgid = msg['id'] print(BREAK) print(f'Id : {msgid}') print(f'Author: {name} ({username}) <{email}>') print(f'Date : {date}') print(msg['message']) def delete_message(self, change, message_id, reason): self.post( f'/a/changes/{change}/messages/{message_id}/delete', {'reason': reason}) print("Deleted") def list_comments(self, change, revision): data = self.get(f'/a/changes/{change}/revisions/{revision}/comments') for path, comments in data.items(): for msg in comments: name = msg['author'].get('name') username = msg['author'].get('username') email = msg['author'].get('email') line = msg.get('line') msgid = msg['id'] print(BREAK) print(f'Id : {msgid}') print(f'Author: {name} ({username}) <{email}>') print(f'File : {path} line {line}') print(msg['message']) def delete_comment(self, change, revision, comment_id, reason): self.post( f'/a/changes/{change}/revisions/{revision}/' f'comments/{comment_id}/delete', {'reason': reason}) print("Deleted") class App: def __init__(self): self.parser = argparse.ArgumentParser() p = self.parser p.add_argument('url', help='Gerrit HTTP url') p.add_argument('username', help='Username of Gerrit administrator') p.add_argument('password', help='Password of Gerrit administrator') subparsers = p.add_subparsers(title='commands', help='valid commands') cmd_list_messages = subparsers.add_parser( 'list-messages', help='List change messages') cmd_list_messages.add_argument('change', help='Change number') cmd_list_messages.set_defaults(func=self.list_messages) cmd_delete_message = subparsers.add_parser( 'delete-message', help='Delete change message') cmd_delete_message.add_argument('change', help='Change number') cmd_delete_message.add_argument('message_id', help='Message ID') cmd_delete_message.add_argument( 'reason', help='Reason for removal (will replace message)') cmd_delete_message.set_defaults(func=self.delete_message) cmd_list_comments = subparsers.add_parser( 'list-comments', help='List change comments') cmd_list_comments.add_argument('change', help='Change number') cmd_list_comments.add_argument('revision', help='Change revision') cmd_list_comments.set_defaults(func=self.list_comments) cmd_delete_comment = subparsers.add_parser( 'delete-comment', help='Delete change comment') cmd_delete_comment.add_argument('change', help='Change number') cmd_delete_comment.add_argument('revision', help='Change revision') cmd_delete_comment.add_argument('comment_id', help='Comment ID') cmd_delete_comment.add_argument( 'reason', help='Reason for removal (will replace comment)') cmd_delete_comment.set_defaults(func=self.delete_comment) args = p.parse_args() self.args = args self.gerrit = Gerrit(args.url, args.username, args.password) if 'func' not in args: p.print_help() sys.exit(1) args.func() def list_messages(self): self.gerrit.list_messages(self.args.change) def delete_message(self): self.gerrit.delete_message(self.args.change, self.args.message_id, self.args.reason) def list_comments(self): self.gerrit.list_comments(self.args.change, self.args.revision) def delete_comment(self): self.gerrit.delete_comment(self.args.change, self.args.revision, self.args.comment_id, self.args.reason) if __name__ == '__main__': App()