Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvement to social meta tags + an open graph image generator #263

Draft
wants to merge 28 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
7675859
Initial concept
inxilpro Oct 14, 2022
00f0bb5
Basic open graph image builder
inxilpro Oct 15, 2022
1cda307
Tweak open graph rendering
inxilpro Oct 15, 2022
8df5305
Working `og:generate-image` command
inxilpro Oct 18, 2022
ce6337e
Add image ID for webdriver targetting
inxilpro Oct 18, 2022
466ee82
Adjust for automatic og images
inxilpro Oct 26, 2022
f0d63f2
Compile Assets
inxilpro Oct 26, 2022
b6751b4
Install composer dependencies in workflow
inxilpro Oct 26, 2022
2c3a547
Merge branch 'dynamic-og-tags' of https://github.com/inxilpro/laravel…
inxilpro Oct 26, 2022
5b0ad8d
Do full setup (not just composer)
inxilpro Oct 26, 2022
8c0ad7d
Configure git to use https when cloning docs
inxilpro Oct 26, 2022
00a125c
Use HTTPS for docs
inxilpro Oct 26, 2022
ca02375
Fix HTTPS URL
inxilpro Oct 26, 2022
68a5f8c
Add torchlight config
inxilpro Oct 26, 2022
c271e01
Setup env
inxilpro Oct 26, 2022
7895c7e
Add open graph images
inxilpro Oct 26, 2022
f5cdf02
Better default code block heuristic
inxilpro Oct 26, 2022
46709b9
Merge branch 'dynamic-og-tags' of https://github.com/inxilpro/laravel…
inxilpro Oct 26, 2022
40e6fbb
Add open graph images
inxilpro Oct 26, 2022
cbbc16d
Implement manifest file
inxilpro Oct 27, 2022
ed65413
Add open graph images
inxilpro Oct 27, 2022
0824614
Only build on push to main
inxilpro Oct 27, 2022
072bd6f
Merge branch 'dynamic-og-tags' of https://github.com/inxilpro/laravel…
inxilpro Oct 27, 2022
57ae840
Workflow optimizations
inxilpro Oct 27, 2022
681ce04
Fix code style
inxilpro Oct 27, 2022
f30ba90
Bump to actions/cache@v3
inxilpro Oct 27, 2022
2adea33
A few more code style items
inxilpro Oct 27, 2022
349c078
Add open graph images
inxilpro Oct 27, 2022
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
76 changes: 76 additions & 0 deletions .github/workflows/build-og-images.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: Build Open Graph Images

on:
push:
branches:
- master
- main
- dynamic-og-tags

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout Code
uses: actions/checkout@v3

- name: Set Up PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.1
extensions: dom, curl, libxml, mbstring, zip, pcntl, bcmath, intl, iconv
tools: composer:v2

- name: Set Up Chrome Driver
uses: nanasess/setup-chromedriver@v1

- name: Start Chrome Driver
run: |
export DISPLAY=:99
chromedriver --port=4444 &
sudo Xvfb -ac :99 -screen 0 1400x1200x24 > /dev/null 2>&1 &

- name: Prepare Composer Cache
id: composer-cache
run: |
echo "CACHE_FILES_DIR=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT

- name: Load Composer Cache
uses: actions/cache@v3
with:
path: ${{ steps.composer-cache.outputs.CACHE_FILES_DIR }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-composer-

- name: Install Dependencies
run: composer install

- name: Set Up Environment
run: |
cp .env.example .env
php artisan key:generate

- name: Clone Latest Docs
run: git clone --single-branch --branch "9.x" https://github.com/laravel/docs.git "resources/docs/9.x"

- name: Start Laravel Server
env:
TORCHLIGHT_HOST: ${{ secrets.TORCHLIGHT_HOST }}
TORCHLIGHT_TOKEN: ${{ secrets.TORCHLIGHT_TOKEN }}
run: php artisan serve --no-reload &

- name: Generate Open Graph Images
env:
TORCHLIGHT_HOST: ${{ secrets.TORCHLIGHT_HOST }}
TORCHLIGHT_TOKEN: ${{ secrets.TORCHLIGHT_TOKEN }}
APP_ENV: local
APP_URL: http://127.0.0.1:8000/
run: php artisan og:generate-images

- name: Commit Open Graph Images
uses: stefanzweifel/git-auto-commit-action@v4
with:
file_pattern: 'public/img/og/*'
commit_message: Add open graph images
212 changes: 212 additions & 0 deletions app/Console/Commands/GenerateOpenGraphImages.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
<?php

namespace App\Console\Commands;

use App\Documentation;
use Facebook\WebDriver\Chrome\ChromeOptions;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\WebDriverBy;
use Illuminate\Console\Command;
use Illuminate\Support\Arr;
use Illuminate\Support\LazyCollection;
use Symfony\Component\Console\Command\SignalableCommandInterface;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
use Throwable;

class GenerateOpenGraphImages extends Command implements SignalableCommandInterface
{

/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'og:generate-images {--page=}';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Generate open graph images for docs pages.';

/**
* The Laravel documentation repository.
*
* @var \App\Documentation
*/
protected $docs;

/**
* The web driver instance.
*
* @var \Facebook\WebDriver\Remote\RemoteWebDriver
*/
protected $driver;

/**
* Hashes to track changed files.
*/
protected $manifest = [];

/**
* Execute the console command.
*
* @param \App\Documentation $docs
*
* @return int
*/
public function handle(Documentation $docs)
{
$this->docs = $docs;
$this->driver = $this->driver();

register_shutdown_function(function() {
$this->quit();
});

$this->loadManifest();

try {
$this->pages()->each(function($name) {
$this->getOutput()->write(str_pad("{$name}:", 30));

try {
$page = $this->docs->get(DEFAULT_VERSION, $name);
$hash = $page->hash();

if ($hash === Arr::get($this->manifest, $name)) {
$this->line('No changes');

return;
}

$this->manifest[$name] = $hash;

$this->screenShotOpenGraphImage(
$name, $destination = public_path($page->openGraph('image', 'img/og/'.$name.'.png'))
);

$this->line("Wrote to <info>{$destination}</info>");
} catch (Throwable $exception) {
$this->error(class_basename($exception).': '.$exception->getMessage());
}
});
}
finally {
$this->quit();
}

$this->writeManifest();

return 0;
}

/**
* Take a screenshot of the open graph image for a page.
*
* @param string $name
* @param string $path
*
* @return void
*/
protected function screenShotOpenGraphImage($name, $path)
{
$this->driver->navigate()->to(url('/og-image/'.$name));

$this->driver->wait()->until(function(RemoteWebDriver $driver) {
return $driver->executeScript('return `complete` === document.readyState');
});

$this->driver->findElement(WebDriverBy::id('og-image'))->takeElementScreenshot($path);
}

/**
* @inheritdoc
*/
public function getSubscribedSignals(): array
{
return [
2, // SIGINT
15, //SIGTERM
];
}

/**
* @inheritdoc
*/
public function handleSignal(int $signal): void
{
$this->quit();
exit(1);
}

/**
* Quit any running chrome driver instances.
*
* @return void
*/
protected function quit()
{
if ($this->driver) {
$this->warn("\nQuitting chromedriver...");
$this->driver->quit();
$this->driver = null;
}
}

/**
* Get a lazy collection of all the pages for the current default version.
*
* @return \Illuminate\Support\LazyCollection
*/
protected function pages()
{
if ($page = $this->option('page')) {
return new LazyCollection([ $page ]);
}

$finder = Finder::create()->files()->in(base_path('resources/docs/'.DEFAULT_VERSION))->sortByModifiedTime()->name('*.md');

return LazyCollection::make($finder->getIterator())->map(function(SplFileInfo $file) {
return $file->getBasename('.md');
});
}

/**
* Get the web driver instance for taking screenshots.
*
* @return \Facebook\WebDriver\Remote\RemoteWebDriver
*/
protected function driver()
{
$options = new ChromeOptions();
$options->addArguments([
'--window-size=1400,1000', // Larger than our og:image size
'--force-device-scale-factor=2.0', // Higher DPI screenshot
]);

$capabilities = DesiredCapabilities::chrome();
$capabilities->setCapability(ChromeOptions::CAPABILITY_W3C, $options);

return RemoteWebDriver::create('http://localhost:4444', $capabilities);
}

protected function loadManifest()
{
$path = public_path('img/og/manifest.json');

if (file_exists($path)) {
$this->manifest = json_decode(file_get_contents($path), true, 2, JSON_THROW_ON_ERROR);
}
}

protected function writeManifest()
{
$path = public_path('img/og/manifest.json');

file_put_contents($path, json_encode($this->manifest, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
}
}
8 changes: 4 additions & 4 deletions app/Documentation.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

namespace App;

use App\Documentation\Page;
use Illuminate\Support\Str;
use Illuminate\Filesystem\Filesystem;
use App\Markdown\GithubFlavoredMarkdownConverter;
use Carbon\CarbonInterval;
use Illuminate\Contracts\Cache\Repository as Cache;
use League\CommonMark\Extension\FrontMatter\Output\RenderedContentWithFrontMatter;

class Documentation
{
Expand Down Expand Up @@ -61,7 +63,7 @@ public function getIndex($version)
*
* @param string $version
* @param string $page
* @return string|null
* @return Page|null
*/
public function get($version, $page)
{
Expand All @@ -71,9 +73,7 @@ public function get($version, $page)
if ($this->files->exists($path)) {
$content = $this->files->get($path);

$content = (new GithubFlavoredMarkdownConverter())->convert($content);

return $this->replaceLinks($version, $content);
return new Page($page, $content, $version);
}

return null;
Expand Down
Loading