From 45a9dc3afeb4a7a1f6b98a21371030698151bfc9 Mon Sep 17 00:00:00 2001 From: Jonathan Danse Date: Mon, 29 Jun 2026 10:25:07 +0200 Subject: [PATCH 1/7] chore: keep superpowers workflow docs local only Co-Authored-By: Claude Opus 4.8 --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index e185660..c888029 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,6 @@ gh_repositories.json newcontributors.json topcompanies.json topcompanies_prs.json + +# Superpowers workflow docs (kept local only) +docs/superpowers/ From ba23b064d919e895f2fad5184d7df6b7352d624d Mon Sep 17 00:00:00 2001 From: Jonathan Danse Date: Mon, 29 Jun 2026 10:29:11 +0200 Subject: [PATCH 2/7] feat: add output-file constants for contribution stats --- src/Command/AbstractCommand.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Command/AbstractCommand.php b/src/Command/AbstractCommand.php index bf33e47..d6d1edf 100644 --- a/src/Command/AbstractCommand.php +++ b/src/Command/AbstractCommand.php @@ -25,6 +25,16 @@ class AbstractCommand extends Command protected const FILE_NEW_CONTRIBUTORS = 'newcontributors.json'; + protected const FILE_ISSUES = 'gh_issues.json'; + + protected const FILE_PULLREQUESTS_ALL = 'gh_pullrequests_all.json'; + + protected const FILE_TOP_REVIEWERS = 'top_reviewers.json'; + + protected const FILE_TOP_ISSUES = 'top_issues.json'; + + protected const FILE_TOP_PULLREQUESTS = 'top_pullrequests.json'; + protected const FILE_GHLOGIN_WO_COMPANY = 'gh_loginsWOCompany.json'; protected const FILE_DATA_COMPANIES = 'var/data/companies.json'; From 2093be413d3a676f4e890837fc62c35b3d42b5c2 Mon Sep 17 00:00:00 2001 From: Jonathan Danse Date: Mon, 29 Jun 2026 10:46:05 +0200 Subject: [PATCH 3/7] feat: add traces:fetch:issues command Co-Authored-By: Claude Opus 4.8 --- bin/console | 2 + src/Command/FetchIssuesCommand.php | 110 +++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 src/Command/FetchIssuesCommand.php diff --git a/bin/console b/bin/console index 172e0e7..a901a31 100644 --- a/bin/console +++ b/bin/console @@ -3,6 +3,7 @@ require_once __DIR__ . '/../vendor/autoload.php'; use PrestaShop\Traces\Command\FetchContributorsCommand; +use PrestaShop\Traces\Command\FetchIssuesCommand; use PrestaShop\Traces\Command\FetchPullRequestsMergedCommand; use PrestaShop\Traces\Command\FetchRepositoriesCommand; use PrestaShop\Traces\Command\GenerateNewContributorsCommand; @@ -20,6 +21,7 @@ if (file_exists(__DIR__.'/../.env')) { $app = new Application(); $app->addCommand(new FetchContributorsCommand()); +$app->addCommand(new FetchIssuesCommand()); $app->addCommand(new FetchPullRequestsMergedCommand()); $app->addCommand(new FetchRepositoriesCommand()); $app->addCommand(new GenerateNewContributorsCommand()); diff --git a/src/Command/FetchIssuesCommand.php b/src/Command/FetchIssuesCommand.php new file mode 100644 index 0000000..58f96dd --- /dev/null +++ b/src/Command/FetchIssuesCommand.php @@ -0,0 +1,110 @@ + + */ + protected array $orgRepositories = []; + + protected function configure(): void + { + $this->setName('traces:fetch:issues') + ->setDescription('Fetch issues (excluding pull requests) from Github') + ->addOption( + 'ghtoken', + null, + InputOption::VALUE_OPTIONAL, + '', + isset($_ENV['GH_TOKEN']) ? (string) $_ENV['GH_TOKEN'] : null + ) + ->addOption('repository', 'r', InputOption::VALUE_OPTIONAL, 'GitHub repository'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + parent::execute($input, $output); + + if (!file_exists(self::FILE_REPOSITORIES)) { + $this->output->writeLn(self::FILE_REPOSITORIES . ' is missing. Please execute `php bin/console traces:fetch:repositories`'); + + return 1; + } + + $repository = $input->getOption('repository'); + if (!empty($repository)) { + $this->orgRepositories = [$repository]; + } else { + $this->orgRepositories = json_decode(file_get_contents(self::FILE_REPOSITORIES) ?: '', true); + } + + $time = time(); + $this->output->writeLn([count($this->orgRepositories) . ' repositories to scan.']); + $this->fetchOrgIssues(); + $this->output->writeLn(['', 'Output generated in ' . (time() - $time) . 's.']); + + return 0; + } + + protected function fetchOrgIssues(): void + { + $issues = []; + if (file_exists(self::FILE_ISSUES)) { + unlink(self::FILE_ISSUES); + } + + foreach ($this->orgRepositories as $repository) { + $this->output->writeLn(['', 'Repository : PrestaShop/' . $repository]); + $graphQL = 'query { + repository(name: "' . $repository . '", owner: "PrestaShop") { + issues(first: 100, after: "%s", states: [OPEN, CLOSED], orderBy: {field: CREATED_AT, direction: DESC}) { + totalCount + pageInfo { + endCursor + hasNextPage + } + nodes { + number + author { + login + } + repository { + name + } + } + } + } + }'; + + $data = []; + $issuesCount = 0; + do { + $afterCursor = $data['data']['repository']['issues']['pageInfo']['endCursor'] ?? ''; + $data = $this->github->apiSearchGraphQL(sprintf($graphQL, $afterCursor)); + $nodes = $data['data']['repository']['issues']['nodes']; + $issuesCount += count($nodes); + + foreach ($nodes as $node) { + $issues[] = [ + 'number' => $node['number'], + 'login' => $node['author']['login'] ?? null, + 'repository' => $node['repository']['name'], + ]; + } + + file_put_contents(self::FILE_ISSUES, json_encode($issues, JSON_PRETTY_PRINT)); + + $this->output->writeLn([ + 'Repository : PrestaShop/' . $repository + . ' > Status: ' . $issuesCount . ' / ' . $data['data']['repository']['issues']['totalCount'] + . ' - Total: ' . count($issues)]); + } while ($data['data']['repository']['issues']['pageInfo']['hasNextPage'] === true); + } + } +} From fe81d1a06ffc3bf89ebe91a5d2b069ddff54a124 Mon Sep 17 00:00:00 2001 From: Jonathan Danse Date: Mon, 29 Jun 2026 10:51:25 +0200 Subject: [PATCH 4/7] feat: add traces:fetch:pullrequests:all command Co-Authored-By: Claude Opus 4.8 --- bin/console | 2 + src/Command/FetchPullRequestsAllCommand.php | 121 ++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 src/Command/FetchPullRequestsAllCommand.php diff --git a/bin/console b/bin/console index a901a31..311f943 100644 --- a/bin/console +++ b/bin/console @@ -4,6 +4,7 @@ require_once __DIR__ . '/../vendor/autoload.php'; use PrestaShop\Traces\Command\FetchContributorsCommand; use PrestaShop\Traces\Command\FetchIssuesCommand; +use PrestaShop\Traces\Command\FetchPullRequestsAllCommand; use PrestaShop\Traces\Command\FetchPullRequestsMergedCommand; use PrestaShop\Traces\Command\FetchRepositoriesCommand; use PrestaShop\Traces\Command\GenerateNewContributorsCommand; @@ -22,6 +23,7 @@ if (file_exists(__DIR__.'/../.env')) { $app = new Application(); $app->addCommand(new FetchContributorsCommand()); $app->addCommand(new FetchIssuesCommand()); +$app->addCommand(new FetchPullRequestsAllCommand()); $app->addCommand(new FetchPullRequestsMergedCommand()); $app->addCommand(new FetchRepositoriesCommand()); $app->addCommand(new GenerateNewContributorsCommand()); diff --git a/src/Command/FetchPullRequestsAllCommand.php b/src/Command/FetchPullRequestsAllCommand.php new file mode 100644 index 0000000..36db487 --- /dev/null +++ b/src/Command/FetchPullRequestsAllCommand.php @@ -0,0 +1,121 @@ + + */ + protected array $orgRepositories = []; + + protected function configure(): void + { + $this->setName('traces:fetch:pullrequests:all') + ->setDescription('Fetch all pull requests (any state) and their reviewers from Github') + ->addOption( + 'ghtoken', + null, + InputOption::VALUE_OPTIONAL, + '', + isset($_ENV['GH_TOKEN']) ? (string) $_ENV['GH_TOKEN'] : null + ) + ->addOption('repository', 'r', InputOption::VALUE_OPTIONAL, 'GitHub repository'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + parent::execute($input, $output); + + if (!file_exists(self::FILE_REPOSITORIES)) { + $this->output->writeLn(self::FILE_REPOSITORIES . ' is missing. Please execute `php bin/console traces:fetch:repositories`'); + + return 1; + } + + $repository = $input->getOption('repository'); + if (!empty($repository)) { + $this->orgRepositories = [$repository]; + } else { + $this->orgRepositories = json_decode(file_get_contents(self::FILE_REPOSITORIES) ?: '', true); + } + + $time = time(); + $this->output->writeLn([count($this->orgRepositories) . ' repositories to scan.']); + $this->fetchOrgPullRequests(); + $this->output->writeLn(['', 'Output generated in ' . (time() - $time) . 's.']); + + return 0; + } + + protected function fetchOrgPullRequests(): void + { + $pullRequests = []; + if (file_exists(self::FILE_PULLREQUESTS_ALL)) { + unlink(self::FILE_PULLREQUESTS_ALL); + } + + foreach ($this->orgRepositories as $repository) { + $this->output->writeLn(['', 'Repository : PrestaShop/' . $repository]); + $graphQL = 'query { + repository(name: "' . $repository . '", owner: "PrestaShop") { + pullRequests(first: 25, after: "%s", states: [OPEN, MERGED, CLOSED], orderBy: {field: CREATED_AT, direction: DESC}) { + totalCount + pageInfo { + endCursor + hasNextPage + } + nodes { + number + author { + login + } + reviews(first: 100) { + nodes { + author { + login + } + } + } + } + } + } + }'; + + $data = []; + $pullRequestsCount = 0; + do { + $afterCursor = $data['data']['repository']['pullRequests']['pageInfo']['endCursor'] ?? ''; + $data = $this->github->apiSearchGraphQL(sprintf($graphQL, $afterCursor)); + $nodes = $data['data']['repository']['pullRequests']['nodes']; + $pullRequestsCount += count($nodes); + + foreach ($nodes as $node) { + $reviewers = []; + foreach ($node['reviews']['nodes'] as $review) { + $reviewerLogin = $review['author']['login'] ?? null; + if ($reviewerLogin !== null) { + $reviewers[$reviewerLogin] = true; + } + } + $pullRequests[] = [ + 'number' => $node['number'], + 'login' => $node['author']['login'] ?? null, + 'reviewers' => array_keys($reviewers), + ]; + } + + file_put_contents(self::FILE_PULLREQUESTS_ALL, json_encode($pullRequests, JSON_PRETTY_PRINT)); + + $this->output->writeLn([ + 'Repository : PrestaShop/' . $repository + . ' > Status: ' . $pullRequestsCount . ' / ' . $data['data']['repository']['pullRequests']['totalCount'] + . ' - Total: ' . count($pullRequests)]); + } while ($data['data']['repository']['pullRequests']['pageInfo']['hasNextPage'] === true); + } + } +} From 551436a28bbb0be4757d53a48b139dc1c2807687 Mon Sep 17 00:00:00 2001 From: Jonathan Danse Date: Mon, 29 Jun 2026 10:58:36 +0200 Subject: [PATCH 5/7] feat: add traces:generate:topstats command Aggregates issues + all-state PRs into per-contributor counters (reviews = distinct PRs reviewed with self-review excluded, issuesOpened, pullRequestsOpened), enriches contributors_prs.json, and writes the three ranking files. Reviews count on every PR regardless of (possibly null) author; only PR/issue author counts skip null authors. Co-Authored-By: Claude Opus 4.8 --- bin/console | 2 + src/Command/GenerateTopStatsCommand.php | 200 ++++++++++++++++++++++++ 2 files changed, 202 insertions(+) create mode 100644 src/Command/GenerateTopStatsCommand.php diff --git a/bin/console b/bin/console index 311f943..923776d 100644 --- a/bin/console +++ b/bin/console @@ -9,6 +9,7 @@ use PrestaShop\Traces\Command\FetchPullRequestsMergedCommand; use PrestaShop\Traces\Command\FetchRepositoriesCommand; use PrestaShop\Traces\Command\GenerateNewContributorsCommand; use PrestaShop\Traces\Command\GenerateTopCompaniesCommand; +use PrestaShop\Traces\Command\GenerateTopStatsCommand; use Symfony\Component\Console\Application; use Symfony\Component\Dotenv\Dotenv; @@ -28,4 +29,5 @@ $app->addCommand(new FetchPullRequestsMergedCommand()); $app->addCommand(new FetchRepositoriesCommand()); $app->addCommand(new GenerateNewContributorsCommand()); $app->addCommand(new GenerateTopCompaniesCommand()); +$app->addCommand(new GenerateTopStatsCommand()); $app->run(); diff --git a/src/Command/GenerateTopStatsCommand.php b/src/Command/GenerateTopStatsCommand.php new file mode 100644 index 0000000..c77113f --- /dev/null +++ b/src/Command/GenerateTopStatsCommand.php @@ -0,0 +1,200 @@ +setName('traces:generate:topstats') + ->setDescription('Generate reviewers/issues/pull-requests leaderboards and enrich contributors_prs.json') + ->addOption( + 'ghtoken', + null, + InputOption::VALUE_OPTIONAL, + '', + isset($_ENV['GH_TOKEN']) ? (string) $_ENV['GH_TOKEN'] : null + ) + ->addOption('config', 'c', InputOption::VALUE_OPTIONAL, '', 'config.dist.yml'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + parent::execute($input, $output); + + // contributors_prs.json is produced by traces:generate:topcompanies, so this + // command must run after it (and after the two fetch commands below). + $requiredFiles = [ + self::FILE_PULLREQUESTS_ALL => 'traces:fetch:pullrequests:all', + self::FILE_ISSUES => 'traces:fetch:issues', + self::FILE_CONTRIBUTORS_PRS => 'traces:generate:topcompanies', + ]; + foreach ($requiredFiles as $required => $command) { + if (!file_exists($required)) { + $this->output->writeLn(sprintf('%s is missing. Please execute `php bin/console %s`', $required, $command)); + + return 1; + } + } + + $this->fetchConfiguration($input->getOption('config')); + + /** @var array}> $pullRequests */ + $pullRequests = json_decode(file_get_contents(self::FILE_PULLREQUESTS_ALL) ?: '', true); + /** @var array $issues */ + $issues = json_decode(file_get_contents(self::FILE_ISSUES) ?: '', true); + /** @var array $contributors */ + $contributors = json_decode(file_get_contents(self::FILE_CONTRIBUTORS_PRS) ?: '', true); + + $aggregate = self::aggregate($pullRequests, $issues); + + $this->enrichContributors($contributors, $aggregate); + file_put_contents(self::FILE_CONTRIBUTORS_PRS, json_encode($contributors, JSON_PRETTY_PRINT)); + + file_put_contents(self::FILE_TOP_REVIEWERS, json_encode($this->buildRanking($aggregate['reviews'], $contributors), JSON_PRETTY_PRINT)); + file_put_contents(self::FILE_TOP_ISSUES, json_encode($this->buildRanking($aggregate['issuesOpened'], $contributors), JSON_PRETTY_PRINT)); + file_put_contents(self::FILE_TOP_PULLREQUESTS, json_encode($this->buildRanking($aggregate['pullRequestsOpened'], $contributors), JSON_PRETTY_PRINT)); + + $this->output->writeLn(['', 'Top stats generated.']); + + return 0; + } + + /** + * @param array}> $pullRequests + * @param array $issues + * + * @return array{reviews: array, issuesOpened: array, pullRequestsOpened: array} + */ + public static function aggregate(array $pullRequests, array $issues): array + { + $reviews = []; + $pullRequestsOpened = []; + $issuesOpened = []; + + foreach ($pullRequests as $pullRequest) { + $author = $pullRequest['login'] ?? null; + if ($author !== null) { + $pullRequestsOpened[$author] = ($pullRequestsOpened[$author] ?? 0) + 1; + } + // A review counts for its reviewer regardless of the PR author (even a + // deleted/null author) — only self-reviews are excluded. + foreach ($pullRequest['reviewers'] as $reviewer) { + if ($reviewer === $author) { + continue; + } + $reviews[$reviewer] = ($reviews[$reviewer] ?? 0) + 1; + } + } + + foreach ($issues as $issue) { + $author = $issue['login'] ?? null; + if ($author !== null) { + $issuesOpened[$author] = ($issuesOpened[$author] ?? 0) + 1; + } + } + + return [ + 'reviews' => $reviews, + 'issuesOpened' => $issuesOpened, + 'pullRequestsOpened' => $pullRequestsOpened, + ]; + } + + /** + * @param array $contributors + * @param array{reviews: array, issuesOpened: array, pullRequestsOpened: array} $aggregate + */ + private function enrichContributors(array &$contributors, array $aggregate): void + { + foreach ($contributors as $login => &$entry) { + if (!is_array($entry) || !isset($entry['login'])) { + continue; + } + $entry['reviews'] = $aggregate['reviews'][$login] ?? 0; + $entry['issuesOpened'] = $aggregate['issuesOpened'][$login] ?? 0; + $entry['pullRequestsOpened'] = $aggregate['pullRequestsOpened'][$login] ?? 0; + } + unset($entry); + } + + /** + * @param array $counts + * @param array $contributors + * + * @return array{updatedAt: string, items: array} + */ + private function buildRanking(array $counts, array $contributors): array + { + $logins = array_keys($counts); + usort($logins, function (string $a, string $b) use ($counts): int { + return ($counts[$b] <=> $counts[$a]) ?: strcmp($a, $b); + }); + + $items = []; + $rank = 1; + foreach ($logins as $login) { + if ($counts[$login] <= 0) { + continue; + } + if (!$this->configKeepExcludedUsers && in_array($login, $this->configExclusions, true)) { + continue; + } + $identity = $this->resolveIdentity($login, $contributors); + $items[] = [ + 'rank' => $rank, + 'login' => $login, + 'name' => $identity['name'], + 'avatar_url' => $identity['avatar_url'], + 'html_url' => $identity['html_url'], + 'count' => $counts[$login], + ]; + if (++$rank > self::TOP_N) { + break; + } + } + + return ['updatedAt' => date('Y-m-d'), 'items' => $items]; + } + + /** + * @param array $contributors + * + * @return array{name:string, avatar_url:string, html_url:string} + */ + private function resolveIdentity(string $login, array $contributors): array + { + if (isset($contributors[$login]) && is_array($contributors[$login])) { + $record = $contributors[$login]; + + return [ + 'name' => (string) ($record['name'] ?? $login), + 'avatar_url' => (string) ($record['avatar_url'] ?? ''), + 'html_url' => (string) ($record['html_url'] ?? 'https://github.com/' . $login), + ]; + } + + try { + $user = $this->github->getUser($login); + + return [ + 'name' => (string) ($user['name'] ?? $login), + 'avatar_url' => (string) ($user['avatar_url'] ?? ''), + 'html_url' => (string) ($user['html_url'] ?? 'https://github.com/' . $login), + ]; + } catch (Throwable) { + return [ + 'name' => $login, + 'avatar_url' => '', + 'html_url' => 'https://github.com/' . $login, + ]; + } + } +} From a7a16470fe3ebc3ca31d97db17cf24c1e5f04077 Mon Sep 17 00:00:00 2001 From: Jonathan Danse Date: Mon, 29 Jun 2026 11:21:54 +0200 Subject: [PATCH 6/7] chore: wire contribution-stats commands into pipeline and docs --- .gitignore | 5 +++++ Makefile | 9 ++++++++- README.md | 13 +++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c888029..6fa637b 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,11 @@ gh_repositories.json newcontributors.json topcompanies.json topcompanies_prs.json +gh_issues.json +gh_pullrequests_all.json +top_reviewers.json +top_issues.json +top_pullrequests.json # Superpowers workflow docs (kept local only) docs/superpowers/ diff --git a/Makefile b/Makefile index 073e4cf..fe9a76f 100644 --- a/Makefile +++ b/Makefile @@ -7,11 +7,18 @@ clean: rm newcontributors.json rm topcompanies.json rm topcompanies_prs.json + rm gh_issues.json + rm gh_pullrequests_all.json + rm top_reviewers.json + rm top_issues.json + rm top_pullrequests.json generate: php bin/console traces:fetch:repositories --config="./config.dist.yml" php bin/console traces:fetch:contributors --config="./config.dist.yml" php bin/console traces:fetch:pullrequests:merged + php bin/console traces:fetch:pullrequests:all + php bin/console traces:fetch:issues php bin/console traces:generate:newcontributors --limitNew=10 --config="./config.dist.yml" php bin/console traces:generate:topcompanies --config="./config.dist.yml" - \ No newline at end of file + php bin/console traces:generate:topstats --config="./config.dist.yml" diff --git a/README.md b/README.md index 9bc928f..9181d9f 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,19 @@ The authentication use a Github Token. # 5- Fetch top companies $ php bin/console traces:generate:topcompanies --config="config.yml" ## Files topcompanies.json and gh_loginsWOCompany.json are generated + + # 6- Fetch all pull requests (any state) and their reviewers + $ php bin/console traces:fetch:pullrequests:all + ## A file gh_pullrequests_all.json is generated + + # 7- Fetch issues (excluding pull requests) + $ php bin/console traces:fetch:issues + ## A file gh_issues.json is generated + + # 8- Generate reviewers/issues/pull-requests leaderboards + $ php bin/console traces:generate:topstats --config="config.yml" + ## Files top_reviewers.json, top_issues.json, top_pullrequests.json are generated + ## and contributors_prs.json is enriched with reviews / issuesOpened / pullRequestsOpened ``` ## Configuring From 8901525e9f19b82aeb31cc40208160fa4fd87377 Mon Sep 17 00:00:00 2001 From: Jonathan Danse Date: Mon, 29 Jun 2026 14:24:54 +0200 Subject: [PATCH 7/7] refactor: apply PR review feedback - fetch:issues / fetch:pullrequests:all: write the output file once after the pagination loop instead of on every page - generate:topstats: inline the required-files map in the foreach - generate:topstats: aggregate() is a plain (non-static) method - drop the docs/superpowers gitignore entry Co-Authored-By: Claude Opus 4.8 --- .gitignore | 3 --- src/Command/FetchIssuesCommand.php | 4 ++-- src/Command/FetchPullRequestsAllCommand.php | 4 ++-- src/Command/GenerateTopStatsCommand.php | 9 ++++----- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 6fa637b..6a2b9cf 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,3 @@ gh_pullrequests_all.json top_reviewers.json top_issues.json top_pullrequests.json - -# Superpowers workflow docs (kept local only) -docs/superpowers/ diff --git a/src/Command/FetchIssuesCommand.php b/src/Command/FetchIssuesCommand.php index 58f96dd..669a5d7 100644 --- a/src/Command/FetchIssuesCommand.php +++ b/src/Command/FetchIssuesCommand.php @@ -98,13 +98,13 @@ protected function fetchOrgIssues(): void ]; } - file_put_contents(self::FILE_ISSUES, json_encode($issues, JSON_PRETTY_PRINT)); - $this->output->writeLn([ 'Repository : PrestaShop/' . $repository . ' > Status: ' . $issuesCount . ' / ' . $data['data']['repository']['issues']['totalCount'] . ' - Total: ' . count($issues)]); } while ($data['data']['repository']['issues']['pageInfo']['hasNextPage'] === true); + + file_put_contents(self::FILE_ISSUES, json_encode($issues, JSON_PRETTY_PRINT)); } } } diff --git a/src/Command/FetchPullRequestsAllCommand.php b/src/Command/FetchPullRequestsAllCommand.php index 36db487..e03fa90 100644 --- a/src/Command/FetchPullRequestsAllCommand.php +++ b/src/Command/FetchPullRequestsAllCommand.php @@ -109,13 +109,13 @@ protected function fetchOrgPullRequests(): void ]; } - file_put_contents(self::FILE_PULLREQUESTS_ALL, json_encode($pullRequests, JSON_PRETTY_PRINT)); - $this->output->writeLn([ 'Repository : PrestaShop/' . $repository . ' > Status: ' . $pullRequestsCount . ' / ' . $data['data']['repository']['pullRequests']['totalCount'] . ' - Total: ' . count($pullRequests)]); } while ($data['data']['repository']['pullRequests']['pageInfo']['hasNextPage'] === true); + + file_put_contents(self::FILE_PULLREQUESTS_ALL, json_encode($pullRequests, JSON_PRETTY_PRINT)); } } } diff --git a/src/Command/GenerateTopStatsCommand.php b/src/Command/GenerateTopStatsCommand.php index c77113f..9548388 100644 --- a/src/Command/GenerateTopStatsCommand.php +++ b/src/Command/GenerateTopStatsCommand.php @@ -31,12 +31,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int // contributors_prs.json is produced by traces:generate:topcompanies, so this // command must run after it (and after the two fetch commands below). - $requiredFiles = [ + foreach ([ self::FILE_PULLREQUESTS_ALL => 'traces:fetch:pullrequests:all', self::FILE_ISSUES => 'traces:fetch:issues', self::FILE_CONTRIBUTORS_PRS => 'traces:generate:topcompanies', - ]; - foreach ($requiredFiles as $required => $command) { + ] as $required => $command) { if (!file_exists($required)) { $this->output->writeLn(sprintf('%s is missing. Please execute `php bin/console %s`', $required, $command)); @@ -53,7 +52,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int /** @var array $contributors */ $contributors = json_decode(file_get_contents(self::FILE_CONTRIBUTORS_PRS) ?: '', true); - $aggregate = self::aggregate($pullRequests, $issues); + $aggregate = $this->aggregate($pullRequests, $issues); $this->enrichContributors($contributors, $aggregate); file_put_contents(self::FILE_CONTRIBUTORS_PRS, json_encode($contributors, JSON_PRETTY_PRINT)); @@ -73,7 +72,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int * * @return array{reviews: array, issuesOpened: array, pullRequestsOpened: array} */ - public static function aggregate(array $pullRequests, array $issues): array + public function aggregate(array $pullRequests, array $issues): array { $reviews = []; $pullRequestsOpened = [];