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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,8 @@ 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
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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"
php bin/console traces:generate:topstats --config="./config.dist.yml"
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions bin/console
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
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;
use PrestaShop\Traces\Command\GenerateTopCompaniesCommand;
use PrestaShop\Traces\Command\GenerateTopStatsCommand;
use Symfony\Component\Console\Application;
use Symfony\Component\Dotenv\Dotenv;

Expand All @@ -20,8 +23,11 @@ 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());
$app->addCommand(new GenerateTopCompaniesCommand());
$app->addCommand(new GenerateTopStatsCommand());
$app->run();
10 changes: 10 additions & 0 deletions src/Command/AbstractCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
110 changes: 110 additions & 0 deletions src/Command/FetchIssuesCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php

namespace PrestaShop\Traces\Command;

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class FetchIssuesCommand extends AbstractCommand
{
/**
* @var array<string>
*/
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'],
];
}

$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));
}
}
}
121 changes: 121 additions & 0 deletions src/Command/FetchPullRequestsAllCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<?php

namespace PrestaShop\Traces\Command;

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class FetchPullRequestsAllCommand extends AbstractCommand
{
/**
* @var array<string>
*/
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),
];
}

$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));
}
}
}
Loading