|
<?php |
|
/** |
|
* Test Contributor Badge Checker |
|
* |
|
* @TODO: |
|
* - Add support for `@first last` Slack names copied directly from chats (e.g. from release parties). |
|
* These would be prefaced by `@`, and may/may not have spaces. |
|
* When encountered, check Slack (API) for the corresponding ID, then pass that to the profiles URL. |
|
* - Check for test reports in GitHub, e.g. in Gutenberg issues. |
|
* - Track who's been badged already between runs of the script, to optimize checks. |
|
*/ |
|
|
|
// Fake out Trac that these are not curl scraping requests 😇. |
|
// Example full user_agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6.1 Safari/605.1.15 |
|
ini_set('user_agent', 'Mozilla/5.0'); |
|
|
|
const MAX_RECORDS = 100; |
|
const DAYS_BACK = 90; |
|
|
|
$badge = 'badge-test-contributor'; |
|
$history = array(); |
|
|
|
// Set contrib list source |
|
$file = 'contribs.txt'; // default; testing |
|
if (isset($argv[1]) && file_exists($argv[1])) { |
|
$file = $argv[1]; |
|
} |
|
|
|
// Load into lines array |
|
$lines = file($file); |
|
|
|
echo 'WPORG | Slack | GitHub | Badged | Reports' . PHP_EOL; |
|
echo '-----------------------------------------' . PHP_EOL; |
|
|
|
// Create list of users and badge status |
|
foreach ($lines as $line) { |
|
if ($line === '') { |
|
continue; |
|
} |
|
|
|
// Check if line needs splitting |
|
$users = array_map('trim', explode(',', $line)); |
|
|
|
foreach ($users as $user) { |
|
$user = clean_username($user); |
|
|
|
// Don't reprocess known users |
|
if (isset($history[$user])) { |
|
continue; |
|
} |
|
|
|
$profile = get_profile($user); |
|
if ($profile) { |
|
// Get username, which might resolve to something different from $user |
|
$username = get_username($profile); |
|
if ($username === 'github') { // means user didn't match in GitHub either, e.g. https://profiles.wordpress.org/github:U036HHRABB8 |
|
echo 'NOT FOUND: ' . $user . PHP_EOL; |
|
continue; |
|
} |
|
$slack_id = get_username_slack($profile); |
|
$github_id = get_username_github($profile); |
|
|
|
// Check if user is badged |
|
$is_badged = check_badge($profile, $badge); |
|
// Check for a recent test report contribution |
|
// Use `from=mm/dd/yyyy` param to offset range; defaults to today's date |
|
$url = sprintf('https://core.trac.wordpress.org/timeline?ticket_details=on&max=%d&authors=%s&daysback=%d', MAX_RECORDS, $user, DAYS_BACK); |
|
$timeline = get_timeline($url); |
|
$has_report = check_reports($timeline); |
|
|
|
// Output results |
|
echo sprintf( |
|
'%s | %s | %s', |
|
$username, |
|
$slack_id ? $slack_id : $username, |
|
$github_id ? $github_id : 'NONE' |
|
); |
|
echo sprintf(' | %s', $is_badged ? '🧪' : '❌'); |
|
echo sprintf(' | %s', $has_report ? '📄' : '❌'); |
|
echo PHP_EOL; |
|
|
|
$history[$username]['badge'] = $is_badged; |
|
$history[$username]['report'] = $has_report; |
|
$history[$username]['slack'] = $slack_id ? $slack_id : $username; |
|
} else { |
|
echo 'ERROR: ' . $user . PHP_EOL; |
|
$history[$user] = false; |
|
} |
|
} |
|
} |
|
|
|
echo '-----------------------------------------' . PHP_EOL; |
|
echo 'Drum roll, please...🥁' . PHP_EOL; |
|
$badges_given = 0; |
|
|
|
// Show users who qualify but haven't been badged yet. |
|
foreach ($history as $user => $data) { |
|
if (! $data['badge'] && $data['report']) { |
|
echo sprintf('Badge Me: %s | @%s', $user, $data['slack']); |
|
echo PHP_EOL; |
|
$badges_given++; |
|
} |
|
} |
|
|
|
// Show final result. |
|
if ($badges_given === 0) { |
|
echo '😢 No badges to give today.' . PHP_EOL; |
|
// Set stdout exit code to 1 to indicate failure. |
|
exit(1); |
|
} else { |
|
echo sprintf('🎉 Badges given: %d', $badges_given); |
|
echo PHP_EOL; |
|
} |
|
|
|
/* ---------------------------------------------------------------- */ |
|
|
|
/* |
|
* Get the user's profile from WPORG or GitHub. Uses the following URLs: |
|
* - https://profiles.wordpress.org/{query} |
|
* - https://profiles.wordpress.org/github:{query} |
|
* Returns profile page content or false. |
|
*/ |
|
function get_profile($user) { |
|
$context = stream_context_create( |
|
array( |
|
'http' => array( |
|
'follow_location' => true, |
|
) |
|
) |
|
); |
|
|
|
// Check native .Org username and Slack ID redirect |
|
$url = get_profile_url($user); |
|
$profile = @file_get_contents($url, false, $context); |
|
|
|
// If it fails, try using the username as a GitHub ID |
|
if ($profile === false) { |
|
$url = get_profile_url_github($user); |
|
$profile = @file_get_contents($url, false, $context); |
|
} |
|
|
|
return $profile; |
|
} |
|
|
|
function get_profile_url($user) { |
|
return sprintf('https://profiles.wordpress.org/%s/', $user); |
|
} |
|
|
|
function get_profile_url_github($user) { |
|
return sprintf('https://profiles.wordpress.org/github:%s/', $user); |
|
} |
|
|
|
function get_timeline($url) { |
|
return @file_get_contents($url . '&format=rss', false); |
|
} |
|
|
|
function check_badge($profile, $badge) { |
|
return strpos($profile, $badge) > 0 ? true : false; |
|
} |
|
|
|
function clean_username($user) { |
|
return str_replace('@', '', $user); |
|
} |
|
|
|
function get_username($profile) { |
|
preg_match('/<meta property="profile:username" content="(\w+)".*?>/i', $profile, $matches); |
|
return isset($matches[1]) ? $matches[1] : false; |
|
} |
|
|
|
function get_username_slack($profile) { |
|
preg_match('/<span class="username-on-slack".*? class="username">@([\w ]+)<\/span>/is', $profile, $matches); |
|
return isset($matches[1]) ? $matches[1] : false; |
|
} |
|
|
|
function get_username_github($profile) { |
|
preg_match('/<li id="user-github".*?<a href="https:\/\/github.com\/([\w-]+)"/is', $profile, $matches); |
|
return isset($matches[1]) ? $matches[1] : false; |
|
} |
|
|
|
/* |
|
* Check for a recent test report contribution based on common tokens found in the timeline. |
|
* Returns true or false. |
|
*/ |
|
function check_reports($timeline) { |
|
// These can be found as `id="TOKEN"` in formatted test reports. |
|
$report_tokens = array( |
|
'id="TestReport', |
|
'id="Environment', |
|
'id="ActualResult', |
|
'id="ReproductionReport', |
|
'id="ExpectedResult', |
|
'id="TestingInstructions', |
|
'id="Stepsto', |
|
); |
|
|
|
foreach ($report_tokens as $token) { |
|
// Return true as soon as we find one. |
|
if (strpos($timeline, $token) > 0) { |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |