Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
26 changes: 9 additions & 17 deletions src/Command/ListTaskCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,30 @@
use App\Entity\Task;
use App\Repository\TaskRepository;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Attribute\Option;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
#[AsCommand(name: 'app:task:list')]
final class ListTaskCommand extends Command
#[AsCommand(name: 'app:task:list', description: 'List scheduled tasks')]
final class ListTaskCommand
{
public function __construct(
private readonly TaskRepository $repository,
) {
parent::__construct();
}

protected function configure(): void
{
$this->addOption('number', null, InputOption::VALUE_REQUIRED, 'The issue number we are interested in', null);
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
/** @var mixed|null $number */
$number = $input->getOption('number');
public function __invoke(
SymfonyStyle $io,
#[Option]
?int $number = null,
): int {
if (null === $number) {
$criteria = [];
} else {
$criteria = ['number' => (int) $number];
$criteria = ['number' => $number];
}

$limit = 100;
Expand All @@ -53,7 +46,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
];
}

$io = new SymfonyStyle($input, $output);
$io->table(['Repo', 'Number', 'Action', 'Verify After'], $rows);
$io->write(sprintf('Total of %d items in the table. ', $taskCount = count($tasks)));
if ($limit === $taskCount) {
Expand Down
50 changes: 22 additions & 28 deletions src/Command/PingStaleIssuesCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,19 @@
use App\Service\RepositoryProvider;
use App\Service\StaleIssueCommentGenerator;
use App\Service\TaskScheduler;
use Symfony\Component\Console\Attribute\Argument;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Attribute\Option;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

/**
* Close issues not been updated in a long while.
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
#[AsCommand(name: 'app:issue:ping-stale')]
final class PingStaleIssuesCommand extends Command
#[AsCommand(name: 'app:issue:ping-stale', description: 'Ping stale issues and schedule them for closing')]
final class PingStaleIssuesCommand
{
public const string MESSAGE_TWO_AFTER = '+2weeks';
public const string MESSAGE_THREE_AND_CLOSE_AFTER = '+2weeks';
Expand All @@ -36,35 +35,30 @@ public function __construct(
private readonly StaleIssueCommentGenerator $commentGenerator,
private readonly LabelApi $labelApi,
) {
parent::__construct();
}

protected function configure(): void
{
$this->addArgument('repository', InputArgument::REQUIRED, 'The full name to the repository, eg symfony/symfony-docs');
$this->addOption('not-updated-for', null, InputOption::VALUE_REQUIRED, 'A string representing a time period to for how long the issue has been stalled.', '12months');
$this->addOption('dry-run', null, InputOption::VALUE_NONE, 'Do a test search without making any comments or changes');
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
/** @var string $repositoryName */
$repositoryName = $input->getArgument('repository');
$repository = $this->repositoryProvider->getRepository($repositoryName);
if (null === $repository) {
public function __invoke(
OutputInterface $output,
#[Argument(description: 'The full name to the repository, eg symfony/symfony-docs')]
string $repository,
#[Option(description: 'A string representing a time period to for how long the issue has been stalled.')]
string $notUpdatedFor = '12months',
#[Option(description: 'Do a test search without making any comments or changes')]
bool $dryRun = false,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from my view, creating a #[MapInput] PingStaleIssuesInput $input would improve readability here, but it's fine if you prefer keeping the flat structure

): int {
$repo = $this->repositoryProvider->getRepository($repository);
if (null === $repo) {
$output->writeln('Repository not configured');

return Command::FAILURE;
}

/** @var string $timeString */
$timeString = $input->getOption('not-updated-for');
$notUpdatedAfter = new \DateTimeImmutable('-'.ltrim($timeString, '-'));
$issues = $this->issueApi->findStaleIssues($repository, $notUpdatedAfter);
$notUpdatedAfter = new \DateTimeImmutable('-'.ltrim($notUpdatedFor, '-'));
$issues = $this->issueApi->findStaleIssues($repo, $notUpdatedAfter);

if ($input->getOption('dry-run')) {
if ($dryRun) {
foreach ($issues as $issue) {
$output->writeln(sprintf('Marking issue #%s as "Stalled". Link https://github.com/%s/issues/%s', $issue['number'], $repository->getFullName(), $issue['number']));
$output->writeln(sprintf('Marking issue #%s as "Stalled". Link https://github.com/%s/issues/%s', $issue['number'], $repo->getFullName(), $issue['number']));
}

return Command::SUCCESS;
Expand All @@ -75,11 +69,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int
* @var array{number: int, name: string, labels: array<int, array{name: string}>} $issue
*/
$comment = $this->commentGenerator->getComment($this->extractType($issue));
$this->issueApi->commentOnIssue($repository, $issue['number'], $comment);
$this->labelApi->addIssueLabel($issue['number'], 'Stalled', $repository);
$this->issueApi->commentOnIssue($repo, $issue['number'], $comment);
$this->labelApi->addIssueLabel($issue['number'], 'Stalled', $repo);

// add a scheduled task to process this issue again after 2 weeks
$this->scheduler->runLater($repository, $issue['number'], Task::ACTION_INFORM_CLOSE_STALE, new \DateTimeImmutable(self::MESSAGE_TWO_AFTER));
$this->scheduler->runLater($repo, $issue['number'], Task::ACTION_INFORM_CLOSE_STALE, new \DateTimeImmutable(self::MESSAGE_TWO_AFTER));
}

return Command::SUCCESS;
Expand Down
21 changes: 8 additions & 13 deletions src/Command/RunTaskCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,25 @@
use App\Service\TaskRunner;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Attribute\Option;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(name: 'app:task:run')]
final class RunTaskCommand extends Command
#[AsCommand(name: 'app:task:run', description: 'Run scheduled tasks')]
final class RunTaskCommand
{
public function __construct(
private readonly TaskRepository $repository,
private readonly TaskRunner $taskRunner,
private readonly LoggerInterface $logger,
) {
parent::__construct();
}

protected function configure(): void
{
$this->addOption('limit', 'l', InputOption::VALUE_REQUIRED, 'Limit the number of tasks to run', '10');
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$limit = (int) $input->getOption('limit');
public function __invoke(
OutputInterface $output,
#[Option(name: 'limit', shortcut: 'l', description: 'Limit the number of tasks to run')]
int $limit = 10,
): int {
foreach ($this->repository->getTasksToVerify($limit) as $task) {
try {
$this->taskRunner->run($task);
Expand Down