Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Command/FetchIssuesCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ protected function fetchOrgIssues(): void
}
nodes {
number
createdAt
author {
login
avatarUrl
Expand Down Expand Up @@ -104,6 +105,7 @@ protected function fetchOrgIssues(): void
'avatar_url' => $author['avatarUrl'] ?? null,
'html_url' => $author['url'] ?? null,
'repository' => $node['repository']['name'],
'createdAt' => $node['createdAt'] ?? null,
];
}

Expand Down
4 changes: 4 additions & 0 deletions src/Command/FetchPullRequestsAllCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ protected function fetchOrgPullRequests(): void
}
nodes {
number
createdAt
author {
login
avatarUrl
Expand All @@ -81,6 +82,7 @@ protected function fetchOrgPullRequests(): void
}
reviews(first: 100) {
nodes {
submittedAt
author {
login
avatarUrl
Expand Down Expand Up @@ -115,6 +117,7 @@ protected function fetchOrgPullRequests(): void
'name' => $reviewer['name'] ?? null,
'avatar_url' => $reviewer['avatarUrl'] ?? null,
'html_url' => $reviewer['url'] ?? null,
'submittedAt' => $review['submittedAt'] ?? null,
];
}
}
Expand All @@ -125,6 +128,7 @@ protected function fetchOrgPullRequests(): void
'name' => $author['name'] ?? null,
'avatar_url' => $author['avatarUrl'] ?? null,
'html_url' => $author['url'] ?? null,
'createdAt' => $node['createdAt'] ?? null,
'reviewers' => array_values($reviewers),
];
}
Expand Down
48 changes: 42 additions & 6 deletions src/Command/GenerateTopCompaniesCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,17 +84,21 @@ protected function execute(InputInterface $input, OutputInterface $output): int
unset($contributors['updatedAt']);
foreach ($contributors as $key => $contributor) {
$contributors[$key]['mergedPullRequests'] = 0;
$contributors[$key]['mergedPullRequestsByYear'] = [];

foreach (self::REPOSITORIES_CATEGORIES as $section => $repositories) {
$contributors[$key]['categories'][$section] = [
'total' => 0,
'byYear' => [],
'repositories' => [],
'repositoriesByYear' => [],
];
foreach ($repositories as $repository) {
$categories[$section]['repositories'][$repository] = 0;
}
}
$contributors[$key]['repositories'] = [];
$contributors[$key]['repositoriesByYear'] = [];
}

$companiesData = json_decode(\file_get_contents(self::FILE_DATA_COMPANIES), true);
Expand Down Expand Up @@ -175,20 +179,32 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return in_array($repository, self::REPOSITORIES_CATEGORIES[$item]) ? $item : $carry;
}, 'others');

++$contributors[$authorLogin]['mergedPullRequests'];
self::bumpPair(
$contributors[$authorLogin]['mergedPullRequests'],
$contributors[$authorLogin]['mergedPullRequestsByYear'],
$yearMerged
);

// Repositoies
if (!array_key_exists($repository, $contributors[$authorLogin]['repositories'])) {
$contributors[$authorLogin]['repositories'][$repository] = 0;
$contributors[$authorLogin]['repositoriesByYear'][$repository] = [];
}
++$contributors[$authorLogin]['repositories'][$repository];
self::bumpPair(
$contributors[$authorLogin]['repositories'][$repository],
$contributors[$authorLogin]['repositoriesByYear'][$repository],
$yearMerged
);
// Section : Total
++$contributors[$authorLogin]['categories'][$section]['total'];
$cat = &$contributors[$authorLogin]['categories'][$section];
self::bumpPair($cat['total'], $cat['byYear'], $yearMerged);
// Section : Repositories
if (!array_key_exists($repository, $contributors[$authorLogin]['categories'][$section]['repositories'])) {
$contributors[$authorLogin]['categories'][$section]['repositories'][$repository] = 0;
if (!array_key_exists($repository, $cat['repositories'])) {
$cat['repositories'][$repository] = 0;
$cat['repositoriesByYear'][$repository] = [];
}
++$contributors[$authorLogin]['categories'][$section]['repositories'][$repository];
self::bumpPair($cat['repositories'][$repository], $cat['repositoriesByYear'][$repository], $yearMerged);
unset($cat);
}

$company = $this->extractCompany($pullRequestData);
Expand All @@ -197,6 +213,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int
} else {
$objCompany = $community;
}
if ($company && $authorLogin !== '') {
$company->contributors[] = $authorLogin;
}
// Company : Total PRs
++$objCompany->mergedPullRequests;
// Company; Total contributions
Expand Down Expand Up @@ -483,6 +502,7 @@ protected function writeFileTopCompaniesByPRs(array $rankedCompanies, Company $c
}

\file_put_contents(self::FILE_TOP_COMPANIES_PRS, json_encode([
'updatedAt' => (new DateTimeImmutable())->format(DATE_ATOM),
'community' => $community->toArray(false),
'companies' => array_map(function (Company $company) {
return $company->toArray(false);
Expand Down Expand Up @@ -581,6 +601,22 @@ protected function writeFileContributorsPRs(array $contributors): void

return ($contributorA['mergedPullRequests'] > $contributorB['mergedPullRequests']) ? -1 : 1;
});
$contributors['updatedAt'] = (new DateTimeImmutable())->format(DATE_ATOM);
\file_put_contents(self::FILE_CONTRIBUTORS_PRS, json_encode($contributors, JSON_PRETTY_PRINT));
}

/**
* Increments a scalar counter AND its parallel year map (additive-strict pattern).
*
* @param array<string, int> $byYear
*/
private static function bumpPair(int &$scalar, array &$byYear, string $year): void
{
++$scalar;
if (!isset($byYear[$year])) {
$byYear[$year] = 0;
krsort($byYear);
}
++$byYear[$year];
}
}
68 changes: 61 additions & 7 deletions src/Command/GenerateTopSecurityCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace PrestaShop\Traces\Command;

use DateTimeImmutable;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
Expand Down Expand Up @@ -45,7 +46,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int

$this->fetchConfiguration($input->getOption('config'));

/** @var array<array{ghsa_id:string|null, credits:array<array{login:string, avatar_url:string|null, html_url:string|null, type:string|null}>}> $advisories */
/** @var array<array{ghsa_id:string|null, published_at:string|null, credits:array<array{login:string, avatar_url:string|null, html_url:string|null, type:string|null}>}> $advisories */
$advisories = json_decode(file_get_contents(self::FILE_SECURITY_ADVISORIES) ?: '', true);
/** @var array<string, mixed> $contributors */
$contributors = file_exists(self::FILE_CONTRIBUTORS_PRS)
Expand All @@ -60,10 +61,45 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}

/**
* @param array<array{ghsa_id:string|null, credits:array<array{login:string, avatar_url:string|null, html_url:string|null, type:string|null}>}> $advisories
* Increments a scalar counter AND its parallel year map (additive-strict pattern).
*
* @param array<string, int> $byYear
*/
private static function bumpPair(int &$scalar, array &$byYear, string $year): void
{
++$scalar;
if (!isset($byYear[$year])) {
$byYear[$year] = 0;
krsort($byYear);
}
++$byYear[$year];
}

/**
* Increments the scalar counter unconditionally, and bumps the year map only
* if $rawDate parses to a valid timestamp. An empty/malformed date must NOT
* fall back to "now" — that would silently misattribute old items to the
* current year and corrupt the byYear breakdown. Skipping the year bump
* (while still counting the scalar) keeps totals correct and honest.
*
* @param array<string, int> $byYear
*/
private static function bumpPairFromDate(int &$scalar, array &$byYear, ?string $rawDate): void
{
$ts = ($rawDate !== null && $rawDate !== '') ? strtotime($rawDate) : false;
if ($ts === false) {
++$scalar;

return;
}
self::bumpPair($scalar, $byYear, date('Y', $ts));
}

/**
* @param array<array{ghsa_id:string|null, published_at:string|null, credits:array<array{login:string, avatar_url:string|null, html_url:string|null, type:string|null}>}> $advisories
* @param array<string, mixed> $contributors
*
* @return array{updatedAt: string, items: array<array{rank:int, login:string, name:string, avatar_url:string, html_url:string, count:int, research:int, remediation:int}>}
* @return array{updatedAt: string, items: array<array{rank:int, login:string, name:string, avatar_url:string, html_url:string, count:int, countByYear:array<string,int>, research:int, researchByYear:array<string,int>, remediation:int, remediationByYear:array<string,int>}>}
*/
public function buildRanking(array $advisories, array $contributors): array
{
Expand All @@ -73,6 +109,7 @@ public function buildRanking(array $advisories, array $contributors): array
$counts = [];
$identities = [];
foreach ($advisories as $advisory) {
$publishedAt = $advisory['published_at'] ?? null;
$perLogin = [];
foreach ($advisory['credits'] as $credit) {
$type = $credit['type'] ?? '';
Expand All @@ -92,9 +129,23 @@ public function buildRanking(array $advisories, array $contributors): array
}
}
foreach ($perLogin as $login => $families) {
$counts[$login]['count'] = ($counts[$login]['count'] ?? 0) + 1;
$counts[$login]['research'] = ($counts[$login]['research'] ?? 0) + ($families['research'] ? 1 : 0);
$counts[$login]['remediation'] = ($counts[$login]['remediation'] ?? 0) + ($families['remediation'] ? 1 : 0);
if (!isset($counts[$login])) {
$counts[$login] = [
'count' => 0,
'countByYear' => [],
'research' => 0,
'researchByYear' => [],
'remediation' => 0,
'remediationByYear' => [],
];
}
self::bumpPairFromDate($counts[$login]['count'], $counts[$login]['countByYear'], $publishedAt);
if ($families['research']) {
self::bumpPairFromDate($counts[$login]['research'], $counts[$login]['researchByYear'], $publishedAt);
}
if ($families['remediation']) {
self::bumpPairFromDate($counts[$login]['remediation'], $counts[$login]['remediationByYear'], $publishedAt);
}
}
}

Expand All @@ -117,12 +168,15 @@ public function buildRanking(array $advisories, array $contributors): array
'avatar_url' => (string) ($contributor['avatar_url'] ?? $identities[$login]['avatar_url']),
'html_url' => (string) ($contributor['html_url'] ?? $identities[$login]['html_url']),
'count' => $counts[$login]['count'],
'countByYear' => $counts[$login]['countByYear'],
'research' => $counts[$login]['research'],
'researchByYear' => $counts[$login]['researchByYear'],
'remediation' => $counts[$login]['remediation'],
'remediationByYear' => $counts[$login]['remediationByYear'],
];
++$rank;
}

return ['updatedAt' => date('Y-m-d'), 'items' => $items];
return ['updatedAt' => (new DateTimeImmutable())->format(DATE_ATOM), 'items' => $items];
}
}
Loading