Skip to content
Merged
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
7 changes: 7 additions & 0 deletions docs/specifications/website.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,13 @@ advertise an enabled web flasher; it MAY explicitly label firmware HIL as
pending. Its mechanically rendered PNG and authored SVG MUST remain paired by
reviewed content and exact-dimension tests.

Every changed social-card byte set MUST use a new content-versioned public
pathname before its metadata is deployed. Replacing a PNG or SVG at an existing
pathname is forbidden because the public CDN may retain the previous bytes
after an origin release changes. Tests MUST bind the metadata URL, local
pathname, dimensions, and reviewed SHA-256; production verification MUST fetch
that exact URL and compare the deployed bytes.

The support route MUST link directly to the preferred GitHub bug template at
`https://github.com/PyBLE-dev/PyBLE/issues/new?template=bug.yml`. Installer
intake MUST request the exact profile ID, board/model and module marking, flash
Expand Down
2 changes: 1 addition & 1 deletion tools/web/src/lib/site.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export function absoluteUrl(path: string): string {
}

export const socialImage = {
url: absoluteUrl("/social/pyble-beta-og-1200x630.png"),
url: absoluteUrl("/social/pyble-beta-og-7e7e037d-1200x630.png"),
width: 1200,
height: 630,
alt: "Actual PyBLE iPad app showing GPIO 48 NeoPixel Blocks and generated MicroPython code",
Expand Down
24 changes: 17 additions & 7 deletions tools/web/src/test/site-contract.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ describe("public-site contract", () => {
});

it("publishes a real-app large social card and local TestFlight card", async () => {
const socialUrl = "https://pyble.dev/social/pyble-beta-og-1200x630.png";
const socialPngName = "pyble-beta-og-7e7e037d-1200x630.png";
const socialSvgName = "pyble-beta-og-48d458bd-1200x630.svg";
const socialUrl = `https://pyble.dev/social/${socialPngName}`;
expect(rootMetadata.openGraph?.images).toEqual([
{
url: socialUrl,
Expand All @@ -100,11 +102,8 @@ describe("public-site contract", () => {

const publicDirectory = join(process.cwd(), "public");
const [socialPng, socialSvg, qrCardPng, qrCardSvg] = await Promise.all([
readFile(join(publicDirectory, "social", "pyble-beta-og-1200x630.png")),
readFile(
join(publicDirectory, "social", "pyble-beta-og-1200x630.svg"),
"utf8",
),
readFile(join(publicDirectory, "social", socialPngName)),
readFile(join(publicDirectory, "social", socialSvgName), "utf8"),
readFile(join(publicDirectory, "social", "pyble-testflight-qr-1080.png")),
readFile(
join(publicDirectory, "social", "pyble-testflight-qr-1080.svg"),
Expand All @@ -118,9 +117,20 @@ describe("public-site contract", () => {
expect(socialSvg).toContain("Everyday coding over BLE.");
expect(socialSvg).toContain("FIRMWARE HIL PENDING");
expect(socialSvg).not.toContain("WEB FLASHER");
expect(createHash("sha256").update(socialPng).digest("hex")).toBe(
const socialPngSha256 = createHash("sha256")
.update(socialPng)
.digest("hex");
const socialSvgSha256 = createHash("sha256")
.update(socialSvg)
.digest("hex");
expect(socialPngSha256).toBe(
"7e7e037d9bd2e58e2e66f516bfd6f1b753bda472c402b8130f8a8a6dd8f19ba9",
);
expect(socialSvgSha256).toBe(
"48d458bd5a6ee1e754bad7d7f9a7261361c0d8a3b13c4fc9cc8a9f5670e04b5d",
);
expect(socialPngName).toContain(socialPngSha256.slice(0, 8));
expect(socialSvgName).toContain(socialSvgSha256.slice(0, 8));
expect([qrCardPng.readUInt32BE(16), qrCardPng.readUInt32BE(20)]).toEqual([
1080, 1080,
]);
Expand Down
Loading