Skip to content

Commit

Permalink
Use events-log API for false positive filtering
Browse files Browse the repository at this point in the history
Signed-off-by: Anisa Su <[email protected]>
  • Loading branch information
anisa-su993 committed Nov 15, 2024
1 parent 90df0b4 commit e373afb
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/dispatch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,14 @@ jobs:
env:
CHANGES_LIMIT: ${{ github.event.inputs.limit || 5 }}
GHPA_TOKEN: ${{ secrets.GHPA_TOKEN }}
GERRIT_PASSWORD: ${{ secrets.GERRIT_PASSWORD}}
LAST_TIMESTAMP: ${{ vars.LAST_TIMESTAMP}}
run: |
./ci/gerrit_changes_to_github.py \
--gerrit-username "spdk-community-ci-samsung" \
--gerrit-api-url "https://review.spdk.io/gerrit/changes/?q=status:open+-age:6mon+repo:spdk/spdk+-label:Community-CI=ANY,user=spdk-community-ci-samsung+-%22%5BRFC%5D%22&o=CURRENT_REVISION" \
--gerrit-events-log-url "https://review.spdk.io/a/plugins/events-log/events/" \
--gh-variables-url "https://api.github.com/repos/spdk-community-ci/dispatcher/actions/variables/" \
--git-repository-path spdk \
--git-remote-target-name origin \
--git-remote-gerrit-name gerrit \
Expand Down
117 changes: 117 additions & 0 deletions gerrit_changes_to_github.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@
that it is set accordingly. This is the name of the CI repository, not the repository
with changes etc.
GERRIT_PASSWORD
Gerrit HTTP Authentication password, needed to access Gerrit's events-log plugin.
LAST_TIMESTAMP
The dispatch workflow pulls events that have occured since LAST_TIMESTAMP from
Gerrit events-log then updates it with the most recent timestamp.
Caveat
------
Expand All @@ -60,6 +67,7 @@
import os
import re
import sys
from datetime import datetime, timezone
from pathlib import Path
from pprint import pprint
from subprocess import CompletedProcess, run
Expand Down Expand Up @@ -157,6 +165,24 @@ def parse_args():
help="Name of the one or more workflow(s) to trigger; e.g. 'myworkflow.yml'",
)

parser.add_argument(
"--gerrit-username",
type=str,
help="Gerrit username of Samsung Bot for Gerrit REST API authorization",
)

parser.add_argument(
"--gerrit-events-log-url",
type=str,
help="URL to utilize to query the Gerrit REST API events-log plugin",
)

parser.add_argument(
"--gh-variables-url",
type=str,
help="GitHub REST API endpoint URL to update dispatcher repository variables",
)

return parser.parse_args()


Expand Down Expand Up @@ -274,6 +300,85 @@ def is_repository_usable(args):
return got_gerrit and got_target


def get_events_from_gerrit(args, gerrit_password, ghpa_token):
"""On success a list of event-dicts are returned. On error, None is returned."""

try:
last_timestamp = int(os.getenv("LAST_TIMESTAMP"))
except Exception as e:
log.error(f"Error reading LAST_TIMESTAMP env. var.; err({e})")
return

since = datetime.fromtimestamp(last_timestamp, tz=timezone.utc).strftime(
"%Y-%m-%d %H:%M:%S"
)
log.info(f"Using last timestamp (UTC): {since}")

# Set t2 cutoff to avoid getting a fraction of events for a particular second
last_timestamp = int(datetime.now().timestamp()) - 1
until = datetime.fromtimestamp(last_timestamp, tz=timezone.utc).strftime(
"%Y-%m-%d %H:%M:%S"
)

if (
response := requests.get(
f"{args.gerrit_events_log_url}/?t1={since};t2={until}",
auth=(args.gerrit_username, gerrit_password),
)
).status_code != 200:
log.error(
f"Failed to retrieve events. HTTP Status Code: {response.status_code}"
)
return None

# Update Github variable LAST_TIMESTAMP
last_timestamp += 1
headers = {
"Accept": "application/vnd.github.v3+json",
"Authorization": f"token {ghpa_token}",
}
data = {"value": f"{last_timestamp}"}

ts_as_str = datetime.fromtimestamp(last_timestamp, tz=timezone.utc).strftime(
"%Y-%m-%d %H:%M:%S"
)
log.info(f"Saving last timestamp (UTC): {ts_as_str}")

if (
requests.patch(
f"{args.gh_variables_url}LAST_TIMESTAMP",
headers=headers,
json=data,
timeout=5,
).status_code
!= 204
):
log.error("Failed to save latest timestamp. Aborting.")
return None

return [json.loads(line) for line in response.text.splitlines()]


def filter_false_positives(events):
"""Returns a list of the corresponding refs of 'comment-added' events where
the comment includes 'false positive'"""

log.info(f"Got {len(events)} events.")

comments = (event for event in events if event.get("comment", None))

false_positives = [
event.get("patchSet").get("ref")
for event in comments
if "false positive" in event.get("comment")
]

if false_positives:
log.info(f"{len(false_positives)} 'false positives' found.")

return false_positives


def main(args):
"""Entry-point for the script; see parse_args() for the arguments"""

Expand Down Expand Up @@ -316,6 +421,18 @@ def main(args):
log.error("Failed filtering changes")
return 1

if (gerrit_password := os.getenv("GERRIT_PASSWORD", None)) is None:
log.error("GERRIT_PASSWORD is not set")

if (events := get_events_from_gerrit(args, gerrit_password, ghpa_token)) is None:
log.error("Failed retrieving events")
return 1

if (false_positives := filter_false_positives(events)) is None:
log.error("Failed retrieving intermittent failures")
return 1

changes_to_push += false_positives
for count, ref in enumerate(changes_to_push, 1):
if count > args.limit:
log.info(f"Pushed count({count}), stopping due to limit({args.limit})")
Expand Down

0 comments on commit e373afb

Please sign in to comment.