diff --git a/.github/workflows/deploy-beta.yml b/.github/workflows/deploy-beta.yml index 91e79fe..1dd6827 100644 --- a/.github/workflows/deploy-beta.yml +++ b/.github/workflows/deploy-beta.yml @@ -29,6 +29,8 @@ jobs: - run: npm ci - run: npm run check - run: npm run build:site + env: + SOCIAL_IMAGE_ORIGIN: https://beta.perryts.com - run: npm run check:blog - name: Deploy beta diff --git a/README.md b/README.md index 8c58fbd..b20bdd0 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,8 @@ The Nginx configuration is in `deploy/nginx-beta.conf`, installed at `/etc/nginx/sites-available/beta.perryts.com` and linked from `sites-enabled`. It uses the existing Certbot certificate, redirects `/` to `/en/`, handles clean URLs, and excludes beta from indexing. Canonical URLs still point to production. +Beta builds set `SOCIAL_IMAGE_ORIGIN=https://beta.perryts.com` so social previews +fetch beta's image. Builds without this override use the production origin. Newsletter requests use a separate `perryts-beta-subscribe` systemd service on loopback port 3852 (`deploy/perryts-beta-subscribe.service`). It reads the existing diff --git a/deploy-beta.sh b/deploy-beta.sh index 2e9fa45..98b6afc 100644 --- a/deploy-beta.sh +++ b/deploy-beta.sh @@ -7,7 +7,7 @@ REMOTE_DIR="/var/www/beta.perryts.com" RELEASE="$(date -u +%Y%m%dT%H%M%SZ)-$(git rev-parse --short HEAD)" case "${1:-}" in - "") npm run build:site ;; + "") SOCIAL_IMAGE_ORIGIN=https://beta.perryts.com npm run build:site ;; --skip-build) ;; # CI has already built and checked this checkout. *) echo "Usage: $0 [--skip-build]" >&2; exit 2 ;; esac diff --git a/public/og.png b/public/og.png index c647f25..2afbdf0 100644 Binary files a/public/og.png and b/public/og.png differ diff --git a/public/perry-social-banner.svg b/public/perry-social-banner.svg index d37a15e..d0328e8 100644 --- a/public/perry-social-banner.svg +++ b/public/perry-social-banner.svg @@ -33,5 +33,5 @@ Perry - TypeScript → Native + TypeScript/JavaScript → Native \ No newline at end of file diff --git a/src/layouts/Base.astro b/src/layouts/Base.astro index 73537ab..b57d269 100644 --- a/src/layouts/Base.astro +++ b/src/layouts/Base.astro @@ -3,7 +3,7 @@ import "../styles/global.css"; import Nav from "../components/Nav.astro"; import Footer from "../components/Footer.astro"; import { locales, localePath, type Locale } from "../i18n/index.ts"; -import { metaDescription, pageTitle } from "../lib/seo.ts"; +import { DEFAULT_SOCIAL_IMAGE, metaDescription, pageTitle, socialImageUrl } from "../lib/seo.ts"; interface Props { lang: Locale; @@ -33,15 +33,17 @@ const { titleIsAbsolute = false, canonical, alternates = locales, - image = "/og.png", + image = DEFAULT_SOCIAL_IMAGE, type = "website", publishedTime, noindex = false, } = Astro.props; const fullTitle = pageTitle(title, titleIsAbsolute); const descriptionText = metaDescription(description); +const socialTitle = fullTitle.replaceAll("JavaScript/TypeScript", "TypeScript/JavaScript"); +const socialDescription = descriptionText.replaceAll("JavaScript/TypeScript", "TypeScript/JavaScript"); const canonicalUrl = new URL(canonical ?? localePath(lang, path), Astro.site); -const imageUrl = new URL(image, Astro.site); +const imageUrl = socialImageUrl(image, Astro.site); const ogLocale = lang.replace("-", "_"); --- @@ -62,19 +64,21 @@ const ogLocale = lang.replace("-", "_"); - - + + - - + + + {image === DEFAULT_SOCIAL_IMAGE && } - - + + {publishedTime && } + {image === DEFAULT_SOCIAL_IMAGE && } diff --git a/src/lib/seo.ts b/src/lib/seo.ts index e989120..00a1c87 100644 --- a/src/lib/seo.ts +++ b/src/lib/seo.ts @@ -2,6 +2,13 @@ const BRAND = " — Perry"; const TITLE_MAX = 60; const DESCRIPTION_MAX = 160; +export const DEFAULT_SOCIAL_IMAGE = "/og.png?v=typescript-javascript"; + +/** Beta serves its own preview image while canonical URLs remain on production. */ +export function socialImageUrl(image: string, site: URL | undefined): URL { + return new URL(image, import.meta.env.SOCIAL_IMAGE_ORIGIN || site); +} + /** * " — Perry". The brand is dropped when the title already names Perry or when it would push * the title past what search results show. diff --git a/src/pages/[lang]/blog/[slug]/index.astro b/src/pages/[lang]/blog/[slug]/index.astro index 39a0f71..0abc893 100644 --- a/src/pages/[lang]/blog/[slug]/index.astro +++ b/src/pages/[lang]/blog/[slug]/index.astro @@ -14,6 +14,7 @@ import { messages, plural } from "../../../../i18n/messages.ts"; import { locales, localePath, type Locale } from "../../../../i18n/index.ts"; import { formatPostDate, getPosts, type Post } from "../../../../lib/blog.ts"; import { readingMinutes } from "../../../../lib/reading-time.ts"; +import { DEFAULT_SOCIAL_IMAGE, socialImageUrl } from "../../../../lib/seo.ts"; // Every post exists under every locale, like the previous site; untranslated ones show English with a notice. export async function getStaticPaths() { @@ -56,7 +57,7 @@ const blogPosting = { keywords: entry.data.tags.join(", "), url, mainEntityOfPage: url, - image: new URL("/og.png", Astro.site).toString(), + image: socialImageUrl(DEFAULT_SOCIAL_IMAGE, Astro.site).toString(), author: { "@type": "Organization", name: "Perry", url: "https://perryts.com/" }, publisher: { "@type": "Organization", name: "Perry", logo: { "@type": "ImageObject", url: new URL("/perry-icon.svg", Astro.site).toString() } }, };