From 01631325d9659c2caf3bdb03ed5067f208bf2638 Mon Sep 17 00:00:00 2001 From: Alexander Gomzyakov Date: Tue, 20 Aug 2024 17:16:41 +0500 Subject: [PATCH] Take issues with `good first issues` labels from the GitHub API and show them (#188) --- Dockerfile | 2 +- README.md | 129 ++++++++++++--- composer.lock | 50 +++--- generate.php | 16 +- repositories.json | 8 - src/DTO/Issue.php | 2 + src/DTO/Repository.php | 53 ------- src/Generator.php | 47 ++---- src/GitHubAPIClient.php | 94 +++-------- src/References/ProgrammingLanguage.php | 21 +++ src/Renderer.php | 83 +++++----- src/Templates/404page.html | 39 ----- src/Templates/Examples/issues.html | 211 ------------------------- src/Templates/Examples/main.html | 210 ------------------------ src/Templates/issue_card.html | 31 ++++ src/Templates/main.html | 82 ++++++++-- src/Templates/main_card.html | 31 ---- src/Templates/main_card_li.html | 6 - 18 files changed, 347 insertions(+), 768 deletions(-) delete mode 100644 repositories.json delete mode 100644 src/DTO/Repository.php create mode 100644 src/References/ProgrammingLanguage.php delete mode 100644 src/Templates/404page.html delete mode 100644 src/Templates/Examples/issues.html delete mode 100644 src/Templates/Examples/main.html create mode 100644 src/Templates/issue_card.html delete mode 100644 src/Templates/main_card.html delete mode 100644 src/Templates/main_card_li.html diff --git a/Dockerfile b/Dockerfile index 82ebc30..3c883e2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM php:8.4.0alpha4-alpine +FROM php:8.3.9-alpine ENV COMPOSER_HOME="/tmp/composer" diff --git a/README.md b/README.md index 7bac179..537bbd8 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ # Good First Issues -**Good First Issues** is an initiative to curate easy pickings from popular projects, so developers who've never contributed to open-source can get started quickly. +**Good First Issues** helps you make your first contribution to open-source. Developers who've never contributed to open-source can get started quickly. Website: [good-first-issues.github.io](https://good-first-issues.github.io) @@ -83,27 +83,112 @@ This website is primarily targeted at developers who want to contribute to open Open-source maintainers are always looking to get more people involved, but new developers generally think it's challenging to become a contributor. We believe getting developers to fix super-easy issues removes the barrier for future contributions. This is why *Good First Issues* exists. -## Adding a new project - -You're welcome to add a new project in *Good First Issues*, just follow these steps: - -- To maintain the quality of projects in *Good First Issues*, please make sure your GitHub repository meets the following criteria: - - - It has at least three issues with the `good first issue` label. This label is already present on all repositories by default. - - - It contains a `README.md` with detailed setup instructions for the project - - - It is actively maintained (last update less than 1 month ago) - -- Add your repository's path (in the format `owner/name` and lexicographic order) in [repositories.json](https://github.com/gomzyakov/good-first-issue/blob/main/repositories.json). - -- Create a new pull-request. Please add the link to the issues page of the repository in the PR description. Once the pull request is merged, the changes will be live on [good-first-issues.github.io](https://good-first-issues.github.io). - -## How does it work? - -- First *Good First Issues* is a static website that uses [PHP](https://www.php.net)` to generate HTML files. -- We use [GitHub REST API](https://docs.github.com/en/rest) to fetch issues from the repositories listed in [repositories.json](https://github.com/gomzyakov/good-first-issue/blob/main/repositories.json). -- To periodically cycle through issues (twice a day), we use [GitHub Workflow](https://docs.github.com/en/actions/using-workflows). +## How it works? + +App send the request to GutHub API for each programming language: + +```bash +curl -L \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/search/issues?q=label:\"good+first+issue\"+language:php+state:open+no:assignee&sort=updated&order=desc&per_page=50&page=1" +``` + +And get the response with JSON-array with information about issues: + +```json +[ + { + "url": "https://api.github.com/repos/symfony/ux/issues/1480", + "repository_url": "https://api.github.com/repos/symfony/ux", + "labels_url": "https://api.github.com/repos/symfony/ux/issues/1480/labels{/name}", + "comments_url": "https://api.github.com/repos/symfony/ux/issues/1480/comments", + "events_url": "https://api.github.com/repos/symfony/ux/issues/1480/events", + "html_url": "https://github.com/symfony/ux/issues/1480", + "id": 2125792255, + "node_id": "I_kwDOEuCKh85-tQP_", + "number": 1480, + "title": "[UX Dropzone] drag'n'drop replacement fail", + "user": { + "login": "3PSY0N", + "id": 78256817, + "node_id": "MDQ6VXNlcjc4MjU2ODE3", + "avatar_url": "https://avatars.githubusercontent.com/u/78256817?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/3PSY0N", + "html_url": "https://github.com/3PSY0N", + "followers_url": "https://api.github.com/users/3PSY0N/followers", + "following_url": "https://api.github.com/users/3PSY0N/following{/other_user}", + "gists_url": "https://api.github.com/users/3PSY0N/gists{/gist_id}", + "starred_url": "https://api.github.com/users/3PSY0N/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/3PSY0N/subscriptions", + "organizations_url": "https://api.github.com/users/3PSY0N/orgs", + "repos_url": "https://api.github.com/users/3PSY0N/repos", + "events_url": "https://api.github.com/users/3PSY0N/events{/privacy}", + "received_events_url": "https://api.github.com/users/3PSY0N/received_events", + "type": "User", + "site_admin": false + }, + "labels": [ + { + "id": 2540405642, + "node_id": "MDU6TGFiZWwyNTQwNDA1NjQy", + "url": "https://api.github.com/repos/symfony/ux/labels/good%20first%20issue", + "name": "good first issue", + "color": "7057ff", + "default": true, + "description": "Good for newcomers" + }, + { + "id": 6831689208, + "node_id": "LA_kwDOEuCKh88AAAABlzNN-A", + "url": "https://api.github.com/repos/symfony/ux/labels/Dropzone", + "name": "Dropzone", + "color": "dddddd", + "default": false, + "description": "" + } + ], + "state": "open", + "locked": false, + "assignee": null, + "assignees": [ + + ], + "milestone": null, + "comments": 5, + "created_at": "2024-02-08T18:08:55Z", + "updated_at": "2024-08-13T11:29:17Z", + "closed_at": null, + "author_association": "NONE", + "active_lock_reason": null, + "body": "Hello,\r\n\r\nWith symfony ux dropzone, you can drag and drop files to add them to the dropzone.\r\nOnce a file is in the zone (file A), if you want to replace it with another file (file B), drag'n'drop doesn't work: file A isn't replaced in the dropzone by file B.\r\nAnd file B opens in a new browser tab.\r\n\r\nIs this a known problem? How can I solve it, please?\r\n\r\n\r\nThanks\r\n\r\n![uxdropzone](https://github.com/symfony/ux/assets/78256817/f9ea1728-4f4b-4287-bcb9-22063b0b47d7)\r\n", + "reactions": { + "url": "https://api.github.com/repos/symfony/ux/issues/1480/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "timeline_url": "https://api.github.com/repos/symfony/ux/issues/1480/timeline", + "performed_via_github_app": null, + "state_reason": null, + "score": 1.0 + }, + { + + } +] +``` + +After that, we just render a couple of static HTML pages. + +To periodically cycle through issues (twice a day), we use [GitHub Workflow](https://docs.github.com/en/actions/using-workflows). ## Help us grow diff --git a/composer.lock b/composer.lock index 7bfc632..1f381fe 100644 --- a/composer.lock +++ b/composer.lock @@ -402,16 +402,16 @@ }, { "name": "nesbot/carbon", - "version": "3.7.0", + "version": "3.8.0", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "cb4374784c87d0a0294e8513a52eb63c0aff3139" + "reference": "bbd3eef89af8ba66a3aa7952b5439168fbcc529f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/cb4374784c87d0a0294e8513a52eb63c0aff3139", - "reference": "cb4374784c87d0a0294e8513a52eb63c0aff3139", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/bbd3eef89af8ba66a3aa7952b5439168fbcc529f", + "reference": "bbd3eef89af8ba66a3aa7952b5439168fbcc529f", "shasum": "" }, "require": { @@ -504,7 +504,7 @@ "type": "tidelift" } ], - "time": "2024-07-16T22:29:20+00:00" + "time": "2024-08-19T06:22:39+00:00" }, { "name": "psr/clock", @@ -1295,26 +1295,26 @@ }, { "name": "composer/pcre", - "version": "3.2.0", + "version": "3.3.0", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "ea4ab6f9580a4fd221e0418f2c357cdd39102a90" + "reference": "1637e067347a0c40bbb1e3cd786b20dcab556a81" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/ea4ab6f9580a4fd221e0418f2c357cdd39102a90", - "reference": "ea4ab6f9580a4fd221e0418f2c357cdd39102a90", + "url": "https://api.github.com/repos/composer/pcre/zipball/1637e067347a0c40bbb1e3cd786b20dcab556a81", + "reference": "1637e067347a0c40bbb1e3cd786b20dcab556a81", "shasum": "" }, "require": { "php": "^7.4 || ^8.0" }, "conflict": { - "phpstan/phpstan": "<1.11.8" + "phpstan/phpstan": "<1.11.10" }, "require-dev": { - "phpstan/phpstan": "^1.11.8", + "phpstan/phpstan": "^1.11.10", "phpstan/phpstan-strict-rules": "^1.1", "phpunit/phpunit": "^8 || ^9" }, @@ -1354,7 +1354,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.2.0" + "source": "https://github.com/composer/pcre/tree/3.3.0" }, "funding": [ { @@ -1370,7 +1370,7 @@ "type": "tidelift" } ], - "time": "2024-07-25T09:36:02+00:00" + "time": "2024-08-19T19:43:53+00:00" }, { "name": "composer/semver", @@ -1732,16 +1732,16 @@ }, { "name": "gomzyakov/php-cs-fixer-config", - "version": "v1.67.0", + "version": "v1.68.0", "source": { "type": "git", "url": "https://github.com/gomzyakov/php-cs-fixer-config.git", - "reference": "e35fd2aaeef615c9563bd0b6092f4a6ab6b021ce" + "reference": "a2314dba4ce7c3aeee56d5f56d2a5b32253f937d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/gomzyakov/php-cs-fixer-config/zipball/e35fd2aaeef615c9563bd0b6092f4a6ab6b021ce", - "reference": "e35fd2aaeef615c9563bd0b6092f4a6ab6b021ce", + "url": "https://api.github.com/repos/gomzyakov/php-cs-fixer-config/zipball/a2314dba4ce7c3aeee56d5f56d2a5b32253f937d", + "reference": "a2314dba4ce7c3aeee56d5f56d2a5b32253f937d", "shasum": "" }, "require": { @@ -1749,7 +1749,7 @@ "php": "^8.3" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.59", + "friendsofphp/php-cs-fixer": "^3.62", "mockery/mockery": "^1.6", "phpstan/phpstan": "^1.11", "phpunit/phpunit": "^11.2" @@ -1779,9 +1779,9 @@ ], "support": { "issues": "https://github.com/gomzyakov/php-cs-fixer-config/issues", - "source": "https://github.com/gomzyakov/php-cs-fixer-config/tree/v1.67.0" + "source": "https://github.com/gomzyakov/php-cs-fixer-config/tree/v1.68.0" }, - "time": "2024-08-02T04:02:17+00:00" + "time": "2024-08-19T08:19:39+00:00" }, { "name": "myclabs/deep-copy", @@ -2021,16 +2021,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.11.10", + "version": "1.11.11", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "640410b32995914bde3eed26fa89552f9c2c082f" + "reference": "707c2aed5d8d0075666e673a5e71440c1d01a5a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/640410b32995914bde3eed26fa89552f9c2c082f", - "reference": "640410b32995914bde3eed26fa89552f9c2c082f", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/707c2aed5d8d0075666e673a5e71440c1d01a5a3", + "reference": "707c2aed5d8d0075666e673a5e71440c1d01a5a3", "shasum": "" }, "require": { @@ -2075,7 +2075,7 @@ "type": "github" } ], - "time": "2024-08-08T09:02:50+00:00" + "time": "2024-08-19T14:37:29+00:00" }, { "name": "phpunit/php-code-coverage", diff --git a/generate.php b/generate.php index 2877956..5ed04d8 100644 --- a/generate.php +++ b/generate.php @@ -8,17 +8,21 @@ use GoodFirstIssue\GitHubAPIClient; use GuzzleHttp\Client; +$clients_config = [ + 'base_uri' => 'https://api.github.com/', +]; + // Get GITHUB_TOKEN from env-variable $gh_token = getenv('GITHUB_TOKEN'); -if (! is_string($gh_token)) { - throw new LogicException('Cannot read GITHUB_TOKEN env-variable'); +if (is_string($gh_token)) { + $clients_config['headers'] = ['Authorization' => 'Bearer ' . $gh_token]; + print_r('Success: ENV-variable GITHUB_TOKEN was set.' . PHP_EOL ); +} else { + print_r('Warning: ENV-variable GITHUB_TOKEN not found.' . PHP_EOL); } // Init Guzzle client with headers -$client = new Client([ - 'base_uri' => 'https://api.github.com/', - 'headers' => ['Authorization' => 'Bearer ' . $gh_token] -]); +$client = new Client($clients_config); $github_api_client = new GitHubAPIClient($client); diff --git a/repositories.json b/repositories.json deleted file mode 100644 index 00e3527..0000000 --- a/repositories.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - "flamenotes/flamenotes", - "larajournal/larajournal", - "gomzyakov/gomzyakov.github.io", - "github-profile-achievements/english", - "github-profile-achievements/russian", - "github-profile-achievements/github-profile-achievements.github.io" -] diff --git a/src/DTO/Issue.php b/src/DTO/Issue.php index 4fb4fce..4fe8a9d 100644 --- a/src/DTO/Issue.php +++ b/src/DTO/Issue.php @@ -10,6 +10,8 @@ public function __construct( public string $html_url, public string $title, public int $number, + public int $comments, + public string $user_avatar_url, public string $updated_at ) { } diff --git a/src/DTO/Repository.php b/src/DTO/Repository.php deleted file mode 100644 index b451938..0000000 --- a/src/DTO/Repository.php +++ /dev/null @@ -1,53 +0,0 @@ - - */ - private array $issues = []; - - /** - * @param string $html_url - * @param string $full_name - * @param string|null $description - * @param string $language - * @param int $stargazers_count - * @param int $open_issues_count - * @param int $open_issues - * @param string $updated_at - */ - public function __construct( - public readonly string $html_url, // Ex: "https://github.com/octocat/Hello-World" - public readonly string $full_name,// Ex: "octocat/Hello-World" - public readonly ?string $description, // Ex: "This your first repo!" - public readonly string $language, // Ex: null, - public readonly int $stargazers_count, // Ex: 80, - public readonly int $open_issues_count, // Ex: 0, - public readonly int $open_issues, // Ex: 0, - public readonly string $updated_at // Ex: "2011-01-26T19:14:43Z", - ) { - } - - /** - * @return array - */ - public function getIssues(): array - { - return $this->issues; - } - - /** - * @param array $issues - * - * @return void - */ - public function setIssues(array $issues): void - { - $this->issues = $issues; - } -} diff --git a/src/Generator.php b/src/Generator.php index 68c60c5..c32edf4 100644 --- a/src/Generator.php +++ b/src/Generator.php @@ -4,8 +4,8 @@ namespace GoodFirstIssue; -use GoodFirstIssue\DTO\Repository; -use LogicException; +use GoodFirstIssue\References\ProgrammingLanguage; +use GuzzleHttp\Exception\GuzzleException; readonly class Generator { @@ -15,45 +15,24 @@ public function __construct( ) { } + /** + * @throws GuzzleException + * + * @return void + */ public function run(): void { if (! is_dir($this->root_path . '/lang')) { mkdir($this->root_path . '/lang'); } - // Get repository names from `repositories.json` file - $json = file_get_contents('repositories.json'); - if (! is_string($json)) { - throw new LogicException('Cannot read file: repositories.json'); - } + // Generate HTML-page with issues for each proramming language + foreach (ProgrammingLanguage::cases() as $language) { + $issues = $this->github_api_client->searchIssuesWithGoodFirtsIssueTag($language->value); + print_r($language->name . ' issues count: ' . count($issues) . PHP_EOL); - $repository_names = json_decode($json, true); - if (! is_array($repository_names)) { - throw new LogicException('Cannot decode repository names'); + $renderer = new Renderer($this->root_path); + $renderer->renderPage($issues, $language); } - - print_r($repository_names); - - $repositories = $this->github_api_client->requestRepositoriesData($repository_names); - - // TODO сортируем репозитории по updated at - // TODO удаляем неактивные репозитории - - foreach ($repositories as $repository) { - print_r('Get issues for repository: ' . $repository->full_name . "\n"); - - $issues = $this->github_api_client->requestIssues($repository->full_name); - $repository->setIssues($issues); - } - - $renderer = new Renderer($this->root_path); - $renderer->renderIndexPage($repositories); - - $repositories_by_language = []; - foreach ($repositories as $repository) { - $repositories_by_language[$repository->language][] = $repository; - } - - // TODO $renderer->renderPerLanguagePage($repositories); } } diff --git a/src/GitHubAPIClient.php b/src/GitHubAPIClient.php index 7657095..cb0cf4d 100644 --- a/src/GitHubAPIClient.php +++ b/src/GitHubAPIClient.php @@ -5,112 +5,56 @@ namespace GoodFirstIssue; use GoodFirstIssue\DTO\Issue; -use GoodFirstIssue\DTO\Repository; use GuzzleHttp\ClientInterface; use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Psr7\Request; use LogicException; -use Throwable; readonly class GitHubAPIClient { - /** - * @param ClientInterface $client - */ public function __construct( private ClientInterface $client, ) { } /** - * Get information about all repositories. + * Search issues with "good firts issue" tag. * - * @param array $repository_names - * - * @return array - */ - public function requestRepositoriesData(array $repository_names): array - { - foreach ($repository_names as $repository_name) { - $repository = $this->requestRepositoryData($repository_name); - - if ($repository !== null) { - $repositories[] = $repository; - } - } - - return $repositories ?? []; - } - - /** - * @param string $repository_name + * @throws GuzzleException * - * @return Repository|null + * @return array */ - public function requestRepositoryData(string $repository_name): ?Repository + public function searchIssuesWithGoodFirtsIssueTag(?string $programming_language = null): array { - $api_route = 'https://api.github.com/repos/' . $repository_name; - $request = new Request('GET', $api_route); + $query_url = 'https://api.github.com/search/issues?q=label:"good+first+issue"+state:open+no:assignee'; - try { - $response = $this->client->send($request); - $repository_json = $response->getBody()->getContents(); - - $repository = json_decode($repository_json, true); - } catch (Throwable $e) { - // TODO Can we open an issue with this problem? - print_r('Not found repository: ' . $repository_name); - print_r($e->getMessage()); - - return null; - } - - if (! is_array($repository)) { - throw new LogicException('Cannot decode repository data'); + if ($programming_language !== null) { + $query_url .= '+language:' . urlencode($programming_language); } - $lang = (strlen((string) $repository['language']) < 1) ? 'other' : $repository['language']; + $query_url .= '&sort=updated&order=desc&per_page=50' + . '&page=1'; // TODO - return new Repository( - $repository['html_url'], - $repository['full_name'], - $repository['description'], - $lang, - $repository['stargazers_count'], - $repository['open_issues_count'], - $repository['open_issues'], - $repository['updated_at'], - ); - } - - /** - * @param string $repository_name - * - * @throws GuzzleException - * - * @return array - */ - public function requestIssues(string $repository_name): array - { - $api_route = 'https://api.github.com/repos/' . $repository_name . '/issues?state=open&sort=updated&labels=good%20first%20issue'; - $request = new Request('GET', $api_route); + $request = new Request('GET', $query_url); $response = $this->client->send($request); $issues_json = $response->getBody()->getContents(); - $issues_data = json_decode($issues_json, true); + $issues_data = json_decode($issues_json, true); if (! is_array($issues_data)) { throw new LogicException('Cannot decode issues data'); } - foreach ($issues_data as $data) { - print_r('Issue #' . $data['number'] . ' ' . $data['title'] . "\n"); + foreach ($issues_data['items'] as $item) { + print_r('Issue #' . $item['number'] . ' ' . $item['title'] . PHP_EOL); $issues[] = new Issue( - $data['html_url'], - $data['title'], - $data['number'], - $data['updated_at'] + $item['html_url'], + $item['title'], + $item['number'], + $item['comments'], + $item['user']['avatar_url'], + $item['updated_at'] ); } diff --git a/src/References/ProgrammingLanguage.php b/src/References/ProgrammingLanguage.php new file mode 100644 index 0000000..b3eaacb --- /dev/null +++ b/src/References/ProgrammingLanguage.php @@ -0,0 +1,21 @@ + $repositories + * @param array $issues + * @param ProgrammingLanguage $language * * @return void */ - public function renderIndexPage(array $repositories): void + public function renderPage(array $issues, ProgrammingLanguage $language): void { $main_html = file_get_contents($template_path = $this->root_path . '/src/Templates/main.html'); @@ -32,73 +34,80 @@ public function renderIndexPage(array $repositories): void } $replace_pairs = [ - '%CARDS%' => $this->renderCardsListHTML($repositories), + '%CARDS%' => $this->renderIssuesListHTML($issues, $language->value), ]; $html = strtr($main_html, $replace_pairs); - file_put_contents('index.html', $html); + if ($language === ProgrammingLanguage::PHP) { + $filename = 'index.html'; + } else { + $filename = $this->root_path . '/lang/' . $language->name . '.html'; + } + + file_put_contents($filename, $html); } /** - * @param array $repositories + * @param array $issues + * @param string $repository_language * * @return string */ - private function renderCardsListHTML(array $repositories): string + private function renderIssuesListHTML(array $issues, string $repository_language): string { $cards_html = ''; - foreach ($repositories as $repository) { - $list_items_html = ''; - foreach ($repository->getIssues() as $issue) { - $list_items_html .= $this->renderCardListItemHTML($issue); - } - - $cards_html .= $this->renderCardHTML($repository, $list_items_html); + foreach ($issues as $issue) { + $cards_html .= $this->renderIssueHTML($issue, $repository_language); } return $cards_html; } - private function renderCardHTML(Repository $repository, string $list_items_html): string + private function renderIssueHTML(Issue $issue, string $repository_language): string { - $main_card_template = file_get_contents($template_path = $this->root_path . '/src/Templates/main_card.html'); + $main_card_template = file_get_contents($template_path = $this->root_path . '/src/Templates/issue_card.html'); if (! is_string($main_card_template)) { throw new LogicException('Cannot read file: ' . $template_path); } $replace_pairs = [ - '%REPO_URL%' => $repository->html_url, - '%REPO_NAME%' => $repository->full_name, - '%REPO_DESCRIPTION%' => $repository->description ?? 'No description', - '_REPO_LANG_' => $repository->language, - '_REPO_STARS_' => $repository->stargazers_count, - '_REPO_LAST_ACTIVITY_' => Carbon::parse($repository->updated_at)->diffForHumans(), - '%ISSUES_LIST_HTML%' => $list_items_html, + '_ISSUE_HREF_' => $issue->html_url, + '_ISSUE_TITLE_' => $issue->title, + '_ISSUE_COMMENTS_' => $issue->comments, + '_ISSUE_UPDATED_AT_' => Carbon::parse($issue->updated_at)->diffForHumans(), + '_USER_AVATAR_URL_' => $issue->user_avatar_url, + '_REPO_LANG_' => $repository_language, + '_REPO_NAME_' => $this->extractRepositoryFullname($issue->html_url), ]; return strtr($main_card_template, $replace_pairs); } - private function renderCardListItemHTML(Issue $issue): string + /** + * Convert `https://github.com/gomzyakov/php-cs-fixer-config/issues/189` to `gomzyakov/php-cs-fixer-config`. + * + * @param string $issue_html_url + * + * @return string + */ + private function extractRepositoryFullname(string $issue_html_url): string { - $list_item_template = file_get_contents($template_path = $this->root_path . '/src/Templates/main_card_li.html'); - - if (! is_string($list_item_template)) { - throw new LogicException('Cannot read file: ' . $template_path); + try { + $string = (string) parse_url($issue_html_url, PHP_URL_PATH); + $string = trim($string, '/'); + $array = explode('/', $string); + + $repository_fullname = $array[0] . '/' . $array[1]; + } catch (Throwable $exception) { + $repository_fullname = 'unknown'; } - $replace_pairs = [ - '_ISSUE_HREF_' => $issue->html_url, - '_ISSUE_TITLE_' => $issue->title, - '_ISSUE_UPDATED_AT_' => Carbon::parse($issue->updated_at)->diffForHumans(), - ]; - - return strtr($list_item_template, $replace_pairs); + return $repository_fullname; } - + // TODO // public function buildLangs(array $repositories): void // { // diff --git a/src/Templates/404page.html b/src/Templates/404page.html deleted file mode 100644 index 1fe0028..0000000 --- a/src/Templates/404page.html +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - 404 Not Found - - - - -
-

404

-

Not Found

-
- - - \ No newline at end of file diff --git a/src/Templates/Examples/issues.html b/src/Templates/Examples/issues.html deleted file mode 100644 index fe5359e..0000000 --- a/src/Templates/Examples/issues.html +++ /dev/null @@ -1,211 +0,0 @@ - - - - - - good first issue - - - - - - -
-
-
- -
-
- - Package tgalopin/html-sanitizer is abandoned, you should avoid using it. Use - symfony/html-sanitizer instead. - -
- - lang: PHP - -   - repo: gomzyakov/good-first-issue -   - - upd: 10 months ago - -
-
-
- -
-
- - Set `level: 2` in phpstan.neon.dist - -
- - lang: PHP - -   - repo: gomzyakov/good-first-issue -   - - upd: 10 months ago - -
-
-
- -
-
- - Fix column styles on md and sm-resolutions - -
- - lang: PHP - -   - repo: gomzyakov/good-first-issue -   - - upd: 3 feb 2023 - -
-
-
- - -
-
- - gomzyakov/good-first-issue - -

- - This is a wider card with supporting text below as a natural lead-in to - additional content. This content is a little bit longer. - -

-
- - lang: C# - -   - stars: 4.17K -   - - last activity: 10 months ago - -
-
- -
- -
-
- - gomzyakov/good-first-issue - -

- - This is a wider card with supporting text below as a natural lead-in to - additional content. This content is a little bit longer. - -

-
- - lang: C# - -   - stars: 4.17K -   - - last activity: 10 months ago - -
-
- -
- -
-
-
About
- -

Good First Issue curates easy pickings from popular open-source projects, and helps you make your first - contribution to open-source.

- - - -

- Made with ♥ by - - Alexander Gomzyakov - -

-
-
-
- - - - \ No newline at end of file diff --git a/src/Templates/Examples/main.html b/src/Templates/Examples/main.html deleted file mode 100644 index cf8651c..0000000 --- a/src/Templates/Examples/main.html +++ /dev/null @@ -1,210 +0,0 @@ - - - - - - Good First Issue: Issues for your first open-source contribution - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
- - gomzyakov/good-first-issue - -

- - This is a wider card with supporting text below as a natural lead-in to - additional content. This content is a little bit longer. - -

-
- - lang: C# - -   - stars: 4.17K -   - - last activity: 10 months ago - -
-
- -
- -
-
- - gomzyakov/good-first-issue - -

- - This is a wider card with supporting text below as a natural lead-in to - additional content. This content is a little bit longer. - -

-
- - lang: C# - -   - stars: 4.17K -   - - last activity: 10 months ago - -
-
- -
- -
-
- - gomzyakov/good-first-issue - -

- - This is a wider card with supporting text below as a natural lead-in to - additional content. This content is a little bit longer. - -

-
- - lang: C# - -   - stars: 4.17K -   - - last activity: 10 months ago - -
-
- -
- -
-
-

Good First Issue

- -

Good First Issue curates easy pickings from popular open-source projects, and helps you make your first - contribution to open-source.

- - - -

- Made with ♥ by - - @gomzyakov - -

-
-
-
- - - - diff --git a/src/Templates/issue_card.html b/src/Templates/issue_card.html new file mode 100644 index 0000000..ce67c0b --- /dev/null +++ b/src/Templates/issue_card.html @@ -0,0 +1,31 @@ +
+
+ + _ISSUE_TITLE_ + +
+ ... +   + + + repo: _REPO_NAME_ + +    + + + updated: _ISSUE_UPDATED_AT_ + +    + + + comments: _ISSUE_COMMENTS_ + +    + + + lang: _REPO_LANG_ + +
+
+
diff --git a/src/Templates/main.html b/src/Templates/main.html index f07b1ad..c1e1c89 100644 --- a/src/Templates/main.html +++ b/src/Templates/main.html @@ -3,8 +3,11 @@ - Good First Issue: Issues for your first open-source contribution + + Good First Issue: Your first open-source contribution + + @@ -16,12 +19,15 @@ + + + @@ -36,7 +42,61 @@ -
+
+ + +
+
+

Good First Issue

+ +

Good First Issue helps you make your first contribution to open-source.

+
+
+ + + + + +
@@ -44,18 +104,20 @@
-

Good First Issue

- -

Good First Issue curates easy pickings from popular open-source projects, and helps you make your first - contribution to open-source.

-
- - Add your project + -

+

+ If you have questions or suggestions (or found a bug), you can always write to + issues + on GitHub. +

+ +

Made with ♥ by @gomzyakov diff --git a/src/Templates/main_card.html b/src/Templates/main_card.html deleted file mode 100644 index c4c6c2e..0000000 --- a/src/Templates/main_card.html +++ /dev/null @@ -1,31 +0,0 @@ -

-
- - %REPO_NAME% - -

- - %REPO_DESCRIPTION% - -

-
- - lang: _REPO_LANG_ - -   - stars: _REPO_STARS_ -   - - last activity: _REPO_LAST_ACTIVITY_ - -
-
- -
diff --git a/src/Templates/main_card_li.html b/src/Templates/main_card_li.html deleted file mode 100644 index 4236cae..0000000 --- a/src/Templates/main_card_li.html +++ /dev/null @@ -1,6 +0,0 @@ -
  • - - _ISSUE_TITLE_ - - _ISSUE_UPDATED_AT_ -