blob: 981ac686882b2defcf998265f8b24a1d46e5bc6d [file] [edit]
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Example CI check script for b4 review tracking.
#
# This script demonstrates the external check tool JSON protocol.
# It reads an RFC 2822 email message from stdin (a single patch or
# cover letter) and prints a JSON array of check results to stdout.
#
# Configure it in your git config as either:
#
# [b4]
# review-perpatch-check-cmd = /path/to/review-ci-example.py
# review-series-check-cmd = /path/to/review-ci-example.py
#
# Each result object has the following fields:
#
# tool (required) Column header name in the results matrix
# status (required) One of "pass", "warn", "fail"
# summary (optional) One-line summary for the detail view
# url (optional) Link for the maintainer to open in a browser
# details (optional) Multi-line text for the scrollable detail view
#
# An empty array [] means nothing to report for this patch.
# A non-zero exit with no JSON output is treated as an execution error.
#
# This example produces random pass/warn/fail results for demonstration.
# Replace the logic below with your actual CI system queries.
#
# Environment variables set by b4:
#
# B4_TRACKING_FILE Path to a temporary JSON file containing the full
# tracking data for the series. Parse it to access
# branch tips, take history, patch metadata, etc.
import email
import json
import os
import random
import sys
def main() -> None:
msg = email.message_from_binary_file(sys.stdin.buffer)
# Underscore-prefixed because this example doesn't actually consume
# `subject` / `branch_tips` further; both ruff F841 and pyright's
# reportUnusedVariable exempt names starting with an underscore.
_subject = msg.get('subject', '(no subject)')
msgid = msg.get('message-id', '').strip('<> ')
# Example: read tracking data for commit-based CI lookups
tracking_file = os.environ.get('B4_TRACKING_FILE', '')
if tracking_file:
with open(tracking_file) as fp:
tracking = json.load(fp)
_branch_tips = tracking.get('series', {}).get('branch-tips', [])
else:
_branch_tips = []
# Seed the RNG with the message-id so results are stable across
# repeated runs of the same message (simulates cached CI results).
random.seed(msgid)
results = []
# Simulate a build check
build_status = random.choice(['pass', 'pass', 'pass', 'warn', 'fail'])
build_result = {
'tool': 'example-build',
'status': build_status,
'summary': {
'pass': 'Build succeeded',
'warn': 'Build succeeded with warnings',
'fail': 'Build failed',
}[build_status],
'url': f'https://ci.example.com/builds/{msgid}',
}
if build_status == 'warn':
build_result['details'] = 'Warning: unused variable in drivers/foo.c:42'
elif build_status == 'fail':
build_result['details'] = (
'Error: implicit declaration of function bar\n drivers/foo.c:57:5'
)
results.append(build_result)
# Simulate a test suite check
test_status = random.choice(['pass', 'pass', 'pass', 'pass', 'warn', 'fail'])
test_result = {
'tool': 'example-test',
'status': test_status,
'summary': {
'pass': '47 tests passed',
'warn': '45 passed, 2 skipped',
'fail': '44 passed, 3 failed',
}[test_status],
'url': f'https://ci.example.com/tests/{msgid}',
}
if test_status == 'fail':
test_result['details'] = (
'FAIL: test_driver_probe (drivers/foo_test.c)\n'
'FAIL: test_driver_remove (drivers/foo_test.c)\n'
'FAIL: test_ioctl_handler (drivers/foo_test.c)'
)
results.append(test_result)
json.dump(results, sys.stdout, indent=2)
sys.stdout.write('\n')
if __name__ == '__main__':
main()