blob: 7d0aec856a9581a6d4486aed8afe582b0b7f5519 [file] [log] [blame]
#!/usr/bin/env python3
# -*- coding: utf-8 -*-"
#
# Copyright 2020 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Module containing methods interfacing with gerrit.
i.e Create new bugfix change tickets, and reading metadata about a specific change.
Example CURL command that creates CL:
curl -b /home/chromeos_patches/.git-credential-cache/cookie \
--header "Content-Type: application/json" \
--data \
'{"project":"chromiumos/third_party/kernel",\
"subject":"test",\
"branch":"chromeos-4.19",\
"topic":"test_topic"}' https://chromium-review.googlesource.com/a/changes/
"""
from __future__ import print_function
import json
import http
import os
import requests
import common
def get_auth_cookie():
"""Load cookies in order to authenticate requests with gerrit/googlesource."""
# This cookie should exist on GCE in order to perform GAIA authenticated requests
gerrit_credentials_cookies = http.cookiejar.MozillaCookieJar(common.GIT_COOKIE_PATH, None, None)
gerrit_credentials_cookies.load()
return gerrit_credentials_cookies
def retrieve_and_parse_endpoint(endpoint_url):
"""Retrieves Gerrit endpoint response and removes XSSI prefix )]}'"""
try:
resp = requests.get(endpoint_url, cookies=get_auth_cookie())
resp.raise_for_status()
resp_json = json.loads(resp.text[5:])
except requests.exceptions.HTTPError as e:
raise type(e)('Endpoint %s should have HTTP response 200' % endpoint_url) from e
except json.decoder.JSONDecodeError as e:
raise ValueError('Response should contain json )]} prefix to prevent XSSI attacks') from e
return resp_json
def set_and_parse_endpoint(endpoint_url, payload):
"""POST request to gerrit endpoint with specified payload."""
try:
resp = requests.post(endpoint_url, json=payload, cookies=get_auth_cookie())
resp.raise_for_status()
resp_json = json.loads(resp.text[5:])
except requests.exceptions.HTTPError as e:
raise type(e)('Endpoint %s should have HTTP response 200' % endpoint_url) from e
except json.decoder.JSONDecodeError as e:
raise ValueError('Response should contain json )]} prefix to prevent XSSI attacks') from e
return resp_json
def get_commit(changeid):
"""Retrieves current commit message for a change.
May add some additional information to the fix patch for tracking purposes.
i.e attaching a tag
"""
get_commit_endpoint = os.path.join(common.CHROMIUM_REVIEW_BASEURL, 'changes',
changeid, 'revisions/current/commit')
resp = retrieve_and_parse_endpoint(get_commit_endpoint)
try:
return resp['message']
except KeyError as e:
raise type(e)('Gerrit API endpoint to get commit should contain message key') from e
def get_changeid_reviewers(changeid):
"""Retrieves list of reviewer emails from gerrit given a chromeos changeid."""
list_reviewers_endpoint = os.path.join(common.CHROMIUM_REVIEW_BASEURL, 'changes',
changeid, 'reviewers')
resp = retrieve_and_parse_endpoint(list_reviewers_endpoint)
try:
return [reviewer_resp['email'] for reviewer_resp in resp]
except KeyError as e:
raise type(e)('Gerrit API endpoint to list reviewers should contain key email') from e
def set_reviewers(changeid, reviewer_emails):
"""Adds reviewers to a Gerrit CL."""
add_reviewer_endpoint = os.path.join(common.CHROMIUM_REVIEW_BASEURL, 'changes',
changeid, 'reviewers')
for email in reviewer_emails:
payload = {'reviewer': email}
set_and_parse_endpoint(add_reviewer_endpoint, payload)
def get_change(changeid):
"""Retrieves ChangeInfo from gerrit using its changeid"""
get_change_endpoint = os.path.join(common.CHROMIUM_REVIEW_BASEURL, 'changes',
changeid)
return retrieve_and_parse_endpoint(get_change_endpoint)
def set_hashtag(changeid):
"""Set hashtag to be autogenerated indicating a robot generated CL."""
set_hashtag_endpoint = os.path.join(common.CHROMIUM_REVIEW_BASEURL, 'changes',
changeid, 'hashtags')
hashtag_input_payload = {'add' : ['autogenerated']}
set_and_parse_endpoint(set_hashtag_endpoint, hashtag_input_payload)
# TODO(hirthanan) implement in seperate CL
def generate_fix_message(fixer_upstream_message):
"""Generates new commit message for a fix change.
Use script ./contrib/from_upstream.py to generate new commit msg
Commit message should include essential information:
i.e:
FROMGIT, FROMLIST, ANDROID, CHROMIUM, etc.
commit message indiciating what is happening
BUG=...
TEST=...
tag for Fixes: <upstream-sha>
"""
print(fixer_upstream_message)
commit_message = ''
return commit_message
# Note: Stable patches won't have a fixee_change_id since they come into chromeos as merges
def create_change(fixer_upstream_commit_message, branch, fixee_changeid=None):
"""Creates a Patch in gerrit given a ChangeInput object."""
create_change_endpoint = os.path.join(common.CHROMIUM_REVIEW_BASEURL, 'changes')
change_input_payload = {'project': common.CHROMEOS_KERNEL_DIR,
'subject': generate_fix_message(fixer_upstream_commit_message),
'branch': common.chromeos_branch(branch)}
resp = set_and_parse_endpoint(create_change_endpoint, change_input_payload)
fixer_changeid = None
try:
fixer_changeid = resp['_number']
except KeyError as e:
raise type(e)('Gerrit API endpoint to create CL should contain key: _number') from e
reviewers = None
if fixee_changeid:
# retrieve reviewers from gerrit for the relevant change
reviewers = get_changeid_reviewers(fixee_changeid)
else:
# TODO(hirthanan): find relevant mailing list/reviewers
# For now we will assign it to a default user like Guenter?
# This is for stable bug fix patches that don't have a direct fixee changeid
# since groups of stable commits get merged as one changeid
reviewers = ['groeck@chromium.org']
set_reviewers(fixer_changeid, reviewers)
set_hashtag(fixer_changeid)
def create_gerrit_change(reviewers, commit_msg):
"""Uses gerrit api to handle creating gerrit change.
Determines whether a change for a fix has already been created,
and avoids duplicate creations.
May add some additional information to the fix patch for tracking purposes.
i.e attaching a tag,
"""
# Call gerrit api to create new change if neccessary
print('Calling gerrit api', reviewers, commit_msg)