diff --git a/config/phpstan/phpstan.neon b/config/phpstan/phpstan.neon index 9b24277b..2c3dc879 100644 --- a/config/phpstan/phpstan.neon +++ b/config/phpstan/phpstan.neon @@ -21,6 +21,7 @@ parameters: - %rootDir%/../../../scripts/process-pending-payments - %rootDir%/../../../scripts/update-ebook-database - %rootDir%/../../../scripts/update-patrons-circle + - %rootDir%/../../../scripts/update-project-commits - %rootDir%/../../../templates dynamicConstantNames: - SITE_STATUS diff --git a/config/sql/se/Projects.sql b/config/sql/se/Projects.sql index 788c5ebf..bece7fbb 100644 --- a/config/sql/se/Projects.sql +++ b/config/sql/se/Projects.sql @@ -12,5 +12,6 @@ CREATE TABLE IF NOT EXISTS `Projects` ( `Ended` datetime DEFAULT NULL, `ManagerUserId` int(11) NOT NULL, `ReviewerUserId` int(11) NOT NULL, + `LastCommitTimestamp` DATETIME NULL DEFAULT NULL, PRIMARY KEY (`ProjectId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; diff --git a/lib/Project.php b/lib/Project.php index bc7eda8e..86693117 100644 --- a/lib/Project.php +++ b/lib/Project.php @@ -25,6 +25,7 @@ class Project{ public ?DateTimeImmutable $Ended = null; public int $ManagerUserId; public int $ReviewerUserId; + public ?DateTimeImmutable $LastCommitTimestamp = null; protected Ebook $_Ebook; protected User $_ManagerUser; @@ -160,7 +161,8 @@ class Project{ Started, Ended, ManagerUserId, - ReviewerUserId + ReviewerUserId, + LastCommitTimestamp ) values ( @@ -175,9 +177,10 @@ class Project{ ?, ?, ?, + ?, ? ) - ', [$this->EbookId, $this->Status, $this->ProducerName, $this->ProducerEmail, $this->DiscussionUrl, $this->VcsUrl, NOW, NOW, $this->Started, $this->Ended, $this->ManagerUserId, $this->ReviewerUserId]); + ', [$this->EbookId, $this->Status, $this->ProducerName, $this->ProducerEmail, $this->DiscussionUrl, $this->VcsUrl, NOW, NOW, $this->Started, $this->Ended, $this->ManagerUserId, $this->ReviewerUserId, $this->LastCommitTimestamp]); $this->ProjectId = Db::GetLastInsertedId(); } @@ -200,10 +203,11 @@ class Project{ Started = ?, Ended = ?, ManagerUserId = ?, - ReviewerUserId = ? + ReviewerUserId = ?, + LastCommitTimestamp = ? where ProjectId = ? - ', [$this->Status, $this->ProducerName, $this->ProducerEmail, $this->DiscussionUrl, $this->VcsUrl, $this->Started, $this->Ended, $this->ManagerUserId, $this->ReviewerUserId, $this->ProjectId]); + ', [$this->Status, $this->ProducerName, $this->ProducerEmail, $this->DiscussionUrl, $this->VcsUrl, $this->Started, $this->Ended, $this->ManagerUserId, $this->ReviewerUserId, $this->LastCommitTimestamp, $this->ProjectId]); if($this->Status == Enums\ProjectStatusType::Abandoned){ Db::Query(' diff --git a/scripts/update-project-commits b/scripts/update-project-commits new file mode 100755 index 00000000..76e33e34 --- /dev/null +++ b/scripts/update-project-commits @@ -0,0 +1,89 @@ +#!/usr/bin/php +VcsUrl); + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, Enums\HttpMethod::Head->value); // Only perform HTTP HEAD. + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); + curl_exec($curl); + + $finalUrl = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL); + // Were we redirected? + if($finalUrl != $project->VcsUrl){ + $project->VcsUrl = $finalUrl; + $project->Save(); + } + + // Now check the actual commits. + $url = preg_replace('|^https://github.com/|iu', 'https://api.github.com/repos/', $project->VcsUrl . '/commits'); + + $curl = curl_init($url); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); + + try{ + $response = curl_exec($curl); + /** @var int $httpCode */ + $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); + + if(!is_string($response)){ + throw new Exception('Response from GitHub was not a string: ' . $response); + } + + if($httpCode != Enums\HttpCode::Ok->value){ + throw new Exception('HTTP code from GitHub was: ' . $httpCode); + } + + /** @var array $commits */ + $commits = json_decode($response); + + if(sizeof($commits) > 0){ + $project->LastCommitTimestamp = new DateTimeImmutable($commits[0]->commit->committer->date); + } + + if($project->LastCommitTimestamp !== null && $project->LastCommitTimestamp < new DateTimeImmutable('30 days ago')){ + $project->Status = Enums\ProjectStatusType::Stalled; + } + else{ + // Revive previously-stalled `Project`s. + $project->Status = Enums\ProjectStatusType::InProgress; + } + + $project->Save(); + } + catch(Exception $ex){ + Log::WriteErrorLogEntry('Error in update-project-commits for URL <' . $url . '>: ' . $ex->getMessage()); + } + + sleep(1); +} diff --git a/templates/ProjectsTable.php b/templates/ProjectsTable.php index 0e323b1f..f11a6469 100644 --- a/templates/ProjectsTable.php +++ b/templates/ProjectsTable.php @@ -4,6 +4,7 @@ */ $includeTitle = $includeTitle ?? true; +$includeStatus = $includeStatus ?? true; ?> @@ -12,9 +13,12 @@ $includeTitle = $includeTitle ?? true; - - + + + + + @@ -26,9 +30,6 @@ $includeTitle = $includeTitle ?? true; Ebook->Title) ?> - - + + + + diff --git a/www/projects/index.php b/www/projects/index.php index 14bbdae3..35ff86aa 100644 --- a/www/projects/index.php +++ b/www/projects/index.php @@ -27,12 +27,12 @@ catch(Exceptions\InvalidPermissionsException){ None.

- $inProgressProjects]) ?> + $inProgressProjects, 'includeStatus' => false]) ?> 0){ ?>

Stalled projects

- $stalledProjects]) ?> + $stalledProjects, 'includeStatus' => false]) ?>
Scroll right →
Title Started ProducerStatusStartedLast commitStatus
- Started->format(Enums\DateTimeFormat::ShortDate->value) ?> - ProducerEmail !== null){ ?> ProducerName) ?> @@ -36,11 +37,19 @@ $includeTitle = $includeTitle ?? true; ProducerName) ?> - Status->GetDisplayName()) ?> + + LastCommitTimestamp?->format(Enums\DateTimeFormat::ShortDate->value) ?> - GitHub repo + Started->format(Enums\DateTimeFormat::ShortDate->value) ?> + + Status->GetDisplayName()) ?> + + GitHub