Portfolio GitHub is a Drupal 11 module that displays cached metadata for a curated list of public GitHub repositories.
The module is intentionally small, but demonstrates production-oriented Drupal backend practices:
- Constructor-injected services and interfaces.
- Validated configuration with complete schema.
- Drupal's HTTP client with bounded requests and typed failures.
- Secrets supplied through an environment variable, never Drupal config.
- Persistent stale snapshots with render cache tags and maximum age.
- Lightweight cron discovery and deduplicated queue processing.
- Classified logging, retry behavior, and negative-result backoff.
- Unit and kernel coverage.
- Drupal 11
- PHP 8.3 or newer
- A host project that exposes the configured token through
$_ENV
A token is optional for public repositories, but avoids GitHub's low unauthenticated rate limit.
Register the public VCS repository and install the module with Composer:
composer config repositories.portfolio-github vcs \
https://github.com/Jonikallio/portfolio_github.git
composer require jonikallio/portfolio_github:dev-mainEnable the module through the Drupal administration interface or Drush:
drush pm:install portfolio_github -yVisit:
/admin/config/services/portfolio-github
Configure:
- Repositories in
owner/repositoryformat. - The environment variable name containing the optional token.
- Render cache maximum age.
- Repository refresh interval.
- HTTP request timeout.
The default token variable name is GITHUB_PORTFOLIO_TOKEN. The token value is
not stored in configuration, logs, cache entries, or render arrays.
Run cron to queue missing or stale repositories:
drush cronPlace the Portfolio GitHub repositories block through Drupal's block layout interface. The block renders stale data during temporary GitHub failures.
The request path reads only normalized snapshots. It does not call GitHub. Snapshots use Drupal key/value storage so deployment-time cache rebuilds do not remove the last successfully fetched data.
cron -> refresh manager -> queue -> GitHub client -> snapshot storage
-> cache-tag invalidation
block -> snapshot storage -> cacheable render array -> Twig
Pending queue markers use Drupal State and a short lock. This prevents repeated cron runs from creating duplicate work while avoiding request-scoped locks as long-lived state.
For a quick code review, start with:
src/Github/GithubClient.php– external API integration and error handlingsrc/Queue/RepositoryRefreshManager.php– cron, queueing, and deduplicationsrc/Plugin/Block/RepositoryPortfolioBlock.php– cacheable Drupal renderingtests/src– unit and kernel test examples
phpcs --standard=phpcs.xml web/modules/custom/portfolio_github
phpstan analyse web/modules/custom/portfolio_github/src --level=max --no-progress
phpunit -c web/core web/modules/custom/portfolio_github/tests/src/UnitKernel tests additionally require SIMPLETEST_DB to point to a disposable test
database or use an isolated table prefix.