| #!/usr/bin/env python |
| # |
| # 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. |
| |
| # This is designed to be called by a gerrit hook. It searched new |
| # patchsets for those from a first time commiter, then posts a helpful |
| # message welcoming them to the community and explaining the review process |
| # |
| # For example, this might be called as follows |
| # python welcome_message.py --change Ia1fea1eab3976f1a9cb89ceb3ce1c6c6a7e79c42 |
| # --change-url \ https://review-dev.openstack.org/81 --project gtest-org/test \ |
| # --branch master --uploader User A. Example (user@example.com) --commit \ |
| # 05508ae633852469d2fd7786a3d6f1d06f87055b --patchset 1 patchset-merged \ |
| # --ssh-user=user --ssh-key=/home/user/.ssh/id_rsa |
| # and if this was the first commit from "user@example.com", a message |
| # would be posted on review 81. |
| |
| |
| import argparse |
| import logging |
| import paramiko |
| |
| import jeepyb.gerritdb |
| import jeepyb.log as l |
| |
| BASE_DIR = '/home/gerrit2/review_site' |
| |
| logger = logging.getLogger('welcome_reviews') |
| |
| |
| def is_newbie(uploader): |
| """Determine if the owner of the patch is a first-timer.""" |
| |
| # Retrieve uploader email |
| try: |
| searchkey = uploader[uploader.rindex("(") + 1:-1] |
| except ValueError: |
| logger.info('Couldnt get email for %s', uploader) |
| return False |
| |
| # this query looks for all distinct patchsets for the given |
| # user. If there's only 1, they're a first-timer. |
| query = """SELECT COUNT(DISTINCT p.change_id + p.patch_set_id) |
| FROM patch_sets p, account_external_ids a |
| WHERE a.email_address = %s |
| AND a.account_id = p.uploader_account_id;""" |
| |
| cursor = jeepyb.gerritdb.connect().cursor() |
| cursor.execute(query, searchkey) |
| data = cursor.fetchone() |
| if data: |
| if data[0] == 1: |
| logger.info('We found a newbie: %s', uploader) |
| return True |
| else: |
| return False |
| |
| |
| def post_message(commit, gerrit_user, gerrit_ssh_key, message_file): |
| """Post a welcome message on the patch set specified by the commit.""" |
| |
| default_text = """\ |
| Congratulations, you've proposed your first change in OpenDev. |
| |
| Your submission will now be tested automatically by Zuul, our gatekeeper, |
| and reviewed by other friendly developers. They will give you feedback and |
| may require you to refine it. |
| |
| People seldom get their patch approved on the first try, so don't be |
| concerned if requested to make corrections. Feel free to modify your patch |
| and resubmit a new change-set. |
| |
| Patches often take days (and sometimes weeks) to get reviewed, so be |
| patient. Don't hesitate to ask for help, and answer questions about your |
| work promptly if you can. The more you get to know reviewers and get to be |
| known by them, the smoother the review and approval process will become. The |
| fastest way to accomplish this is by reviewing other proposed changes |
| yourself: anybody can do it, and it's a great way to learn the code base. |
| |
| Thanks again for participating in OpenDev, we look forward to seeing you |
| around. |
| |
| Workflow Guide: https://docs.openstack.org/infra/manual/developers.html |
| """ |
| |
| if message_file: |
| try: |
| with open(message_file, 'r') as _file: |
| welcome_text = _file.read() |
| except (OSError, IOError): |
| logger.exception("Could not open message file") |
| welcome_text = default_text |
| else: |
| welcome_text = default_text |
| |
| # post the above message, using ssh. |
| command = ('gerrit review ' |
| '--message="{message}" {commit}').format( |
| message=welcome_text, |
| commit=commit) |
| logger.info('Welcoming: %s', commit) |
| ssh = paramiko.SSHClient() |
| ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) |
| ssh.connect('localhost', username=gerrit_user, |
| key_filename=gerrit_ssh_key, port=29418) |
| stdin, stdout, stderr = ssh.exec_command(command) |
| stdout_text = stdout.read() |
| stderr_text = stderr.read() |
| ssh.close() |
| if stdout_text: |
| logger.debug('stdout: %s' % stdout_text) |
| if stderr_text: |
| logger.error('stderr: %s' % stderr_text) |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser() |
| parser.add_argument('hook') |
| # common |
| parser.add_argument('--change', default=None) |
| parser.add_argument('--change-url', default=None) |
| parser.add_argument('--project', default=None) |
| parser.add_argument('--branch', default=None) |
| parser.add_argument('--commit', default=None) |
| parser.add_argument('--topic', default=None) |
| parser.add_argument('--change-owner', default=None) |
| # patchset-abandoned |
| parser.add_argument('--abandoner', default=None) |
| parser.add_argument('--reason', default=None) |
| # change-merged |
| parser.add_argument('--submitter', default=None) |
| parser.add_argument('--newrev', default=None) |
| # patchset-created |
| parser.add_argument('--uploader', default=None) |
| parser.add_argument('--patchset', default=None) |
| parser.add_argument('--is-draft', default=None) |
| parser.add_argument('--kind', default=None) |
| # for Welcome Message |
| parser.add_argument('--ssh-user', dest='ssh_user', |
| help='The gerrit welcome message user') |
| parser.add_argument('--ssh-key', dest='ssh_key', |
| help='The gerrit welcome message SSH key file') |
| parser.add_argument('--message-file', dest='message_file', default=None, |
| help='The gerrit welcome message') |
| # Don't actually post the message |
| parser.add_argument('--dryrun', dest='dryrun', action='store_true') |
| parser.add_argument('--no-dryrun', dest='dryrun', action='store_false') |
| parser.set_defaults(dryrun=False) |
| l.setup_logging_arguments(parser) |
| |
| args = parser.parse_args() |
| |
| l.configure_logging(args) |
| |
| # they're a first-timer, post the message on 1st patchset |
| if is_newbie(args.uploader) and args.patchset == '1' and not args.dryrun: |
| post_message(args.commit, args.ssh_user, args.ssh_key, |
| args.message_file) |
| |
| if __name__ == "__main__": |
| main() |