Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
14 changes: 14 additions & 0 deletions layouts/_default/baseof.html
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,20 @@ <h1>Redirecting&hellip;</h1>

<title>{{ .Page.Title | default "Git" }}{{ if (isset .Params "subtitle") }} - {{ .Params.subtitle }}{{ end }}</title>

<meta property="og:site_name" content="Git" />
<meta property="og:title" content="{{ .Page.Title | default "Git" }}{{ if (isset .Params "subtitle") }} - {{ .Params.subtitle }}{{ end }}" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://git-scm.com{{ .Page.RelPermalink }}" />
<meta property="og:image" content="https://git-scm.com/images/og-image.png" />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:description" content="Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency." />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="{{ .Page.Title | default "Git" }}{{ if (isset .Params "subtitle") }} - {{ .Params.subtitle }}{{ end }}" />
<meta name="twitter:description" content="Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency." />
<meta name="twitter:image" content="https://git-scm.com/images/og-image.png" />

<link href="{{ relURL "favicon.ico" }}" rel='shortcut icon' type='image/x-icon'>

{{ $style := resources.Get "sass/application.scss" | resources.ExecuteAsTemplate "application.scss" . | css.Sass | resources.Minify | fingerprint }}
Expand Down
114 changes: 114 additions & 0 deletions script/take-og-image.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#!/usr/bin/env node
// @ts-check

/**
* Generate a 1200x630 Open Graph image for git-scm.com by taking a real
* screenshot of the built site, so the card reflects the site's actual look
* and feel.
*
* Usage:
* node script/take-og-image.js [worktree-path]
*
* The optional WORKTREE-PATH points at a git-scm.com worktree (default: the
* current directory). The script builds the site with Hugo, serves the built
* site with script/serve-public.js, captures the homepage in light mode at a
* 1200x630 clip, and writes the result to static/images/og-image.png in that
* worktree.
*
* This mirrors the repo's existing screenshot tooling
* (script/compare-screenshots.js) — same build/serve helpers, same Playwright
* stack — so the OG image is a genuine snapshot of the site rather than a
* hand-drawn card that would look nothing like git-scm.com.
*/

const { chromium } = require('@playwright/test');
const { spawn, execSync } = require('child_process');
const fs = require('fs');
const path = require('path');

const WORKTREE = path.resolve(process.argv[2] || '.');
const PORT = 5100;
const VIEWPORT = { width: 1280, height: 720 };
// 1200x630 social card: header + hero + the first two rows of the feature
// grid. The homepage's bottom half (release box and lower cards) is hidden so
// the card never shows a stale release version and nothing is cut off at the
// bottom edge.
const CLIP = { x: 40, y: 0, width: 1200, height: 630 };
const OUTPUT = path.join(WORKTREE, 'static', 'images', 'og-image.png');

/**
* Compose the screenshot region: hide the ever-changing release box (which
* hard-codes the latest version number) plus lower-page content so the clip
* reads as a complete, timeless view of the site.
*/
async function composeForScreenshot(page) {
await page.evaluate(() => {
const hide = (sel) => document.querySelectorAll(sel).forEach((el) => { el.style.display = 'none'; });
hide('#front-downloads'); // "Latest source release" box with version number
hide('#front-book'); // Pro Git promo row below the feature grid
// Keep only the first two feature-grid rows so the 1200x630 clip ends on a
// clean boundary instead of slicing the "Install"/"Community" row.
const lis = document.querySelectorAll('#front-nav ul li');
for (let i = 4; i < lis.length; i++) lis[i].style.display = 'none';
const ul = document.querySelector('#front-nav ul');
if (ul) ul.style.paddingBottom = '24px';
});
}

async function startServer() {
console.error(`Building Hugo site in ${WORKTREE}...`);
execSync('hugo', { cwd: WORKTREE, stdio: 'inherit' });

const serverScript = path.join(WORKTREE, 'script', 'serve-public.js');
const server = spawn('node', [serverScript], {
cwd: WORKTREE,
env: { ...process.env, PORT: String(PORT) },
stdio: ['ignore', 'pipe', 'inherit'],
});

await new Promise((resolve, reject) => {
const timeout = setTimeout(() => reject(new Error('Server startup timeout')), 30000);
server.stdout.on('data', (data) => {
if (data.toString().includes('Now listening')) {
clearTimeout(timeout);
resolve();
}
});
server.on('error', (err) => {
clearTimeout(timeout);
reject(err);
});
});

return server;
}

async function main() {
const server = await startServer();
const browser = await chromium.launch();

try {
const context = await browser.newContext({
viewport: VIEWPORT,
colorScheme: 'light',
});
const page = await context.newPage();

console.error(`Navigating to: http://localhost:${PORT}/`);
await page.goto(`http://localhost:${PORT}/`, { waitUntil: 'networkidle' });

await composeForScreenshot(page);

fs.mkdirSync(path.dirname(OUTPUT), { recursive: true });
await page.screenshot({ path: OUTPUT, clip: CLIP });
console.error(`Saved: ${OUTPUT} (${CLIP.width}x${CLIP.height} clip of the homepage)`);
} finally {
await browser.close();
server.kill();
}
}

main().catch((err) => {
console.error(err);
process.exit(1);
});
Binary file added static/images/og-image.png

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hmm. This screenshot feels like it was taken last millenium... Maybe we could have a more current one?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The new image looks better, but it 1) is cut off at the bottom, and 2) contains the hard-coded version v2.56.0 (which won't age well)...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Two concerns, and a suggestion: 1) now the part where the version was looks a bit empty, oddly so, and 2) it looks as if the PR branch was never rewritten, so that the faulty commits stay there forever, whereas I really like the PR branches to be rewritten before merging (but then, I am not the only person merging here, so you might find someone else willing to merge in the current form). Suggestion: how about pulling the version back in, but updating the og-image.png as part of the script that updates hugo.yml whenever a new Git version comes out, as a separate commit?

Also, I wonder whether OpenGraph has anything for dark mode?

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading