From c3993862d7318fe0c3c2aaee2364e09eafc097d3 Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Mon, 13 Apr 2026 22:56:05 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=91=B7=20Skip=20website=20prebuild=20remo?= =?UTF-8?q?te=20fetches=20on=20cloud=20Claude=20Code=20(#6852)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `website` prebuild (`optimize-images.mjs` + `sync:sponsors`) downloads avatars, out-sourced static assets and the sponsors SVG from GitHub. The cloud-hosted Claude Code sandbox (claude.ai/code) blocks those outbound calls, so the whole `pnpm --filter website build` aborts before `docusaurus build` can even run — preventing MDX content from being validated in that environment. Gate every remote fetch on `process.env.CLAUDE_CODE_REMOTE === 'true'`: when set (cloud Claude Code only), write small placeholder files at the expected output paths instead of calling the network. Local Claude Code CLI, developer machines and CI are unaffected — `CLAUDE_CODE_REMOTE` is not set there, so the original fetch-and-SHA1-verify path still runs byte-for-byte. The local MDX validation in `assessMissingSocialImage()` is preserved unchanged in both modes. Also inline the former `sync:sponsors` bash one-liner into `optimize-images.mjs` so a single env-var gate covers all remote fetching and the build pipeline has one fewer hop. https://claude.ai/code/session_01GfRHx2CMQy1iXJAS27HAyk Co-authored-by: Claude --- website/package.json | 5 ++- website/prebuild/optimize-images.mjs | 54 +++++++++++++++++++++++++--- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/website/package.json b/website/package.json index b837b33e..a0601b10 100644 --- a/website/package.json +++ b/website/package.json @@ -3,9 +3,8 @@ "name": "website", "scripts": { "docusaurus": "docusaurus", - "sync:sponsors": "[ -f static/img/sponsors.svg ] || curl -o static/img/sponsors.svg https://raw.githubusercontent.com/dubzzz/sponsors-svg/main/sponsorkit/sponsors.svg", - "start": "pnpm run sync:sponsors && cp ../.all-contributorsrc src/components/HomepageContributors/all-contributors.json && node prebuild/optimize-images.mjs && docusaurus start", - "build": "pnpm run sync:sponsors && cp ../.all-contributorsrc src/components/HomepageContributors/all-contributors.json && node prebuild/optimize-images.mjs && docusaurus build", + "start": "cp ../.all-contributorsrc src/components/HomepageContributors/all-contributors.json && node prebuild/optimize-images.mjs && docusaurus start", + "build": "cp ../.all-contributorsrc src/components/HomepageContributors/all-contributors.json && node prebuild/optimize-images.mjs && docusaurus build", "swizzle": "docusaurus swizzle", "deploy": "docusaurus deploy", "clear": "docusaurus clear", diff --git a/website/prebuild/optimize-images.mjs b/website/prebuild/optimize-images.mjs index f39472cd..d0d3884e 100644 --- a/website/prebuild/optimize-images.mjs +++ b/website/prebuild/optimize-images.mjs @@ -5,6 +5,19 @@ import path, { join } from 'path'; import { createHash } from 'crypto'; import allContributors from '../src/components/HomepageContributors/all-contributors.json' with { type: 'json' }; +// Detect cloud-hosted Claude Code (claude.ai/code): outbound network calls to +// GitHub are blocked in that sandbox, so we skip remote fetches entirely and +// write placeholder files so that `docusaurus build` can still run and the +// local-only MDX validation (`assessMissingSocialImage`) still executes. +// Every other environment (local Claude Code CLI, developer machines, CI) +// keeps the original strict fetch-and-verify behavior untouched. +const IS_CLOUD_CLAUDE = process.env.CLAUDE_CODE_REMOTE === 'true'; + +async function writePlaceholderImage(finalPath, size) { + const img = new Jimp({ width: size, height: size, color: 0xccccccff }); + await img.write(finalPath, { quality: 80 }); +} + // Collecting AVATARs for contributors async function collectAvatar(imageUrl, imageFinalPath, squaredSize) { @@ -43,9 +56,14 @@ async function syncAvatars() { const { url, login, size } = avatar; const pathFinalImage = join(pathFinalAvatarDirectory, `avatar_${size}_${login}.jpg`); if (!existsSync(pathFinalImage)) { - console.log(`Importing avatar ${size}x${size} for ${url}`); mkdirSync(pathFinalAvatarDirectory, { recursive: true }); - await collectAvatar(url, pathFinalImage, 64); + if (IS_CLOUD_CLAUDE) { + console.log(`[prebuild] Cloud Claude Code detected, writing placeholder for avatar ${size}x${size} for ${url}`); + await writePlaceholderImage(pathFinalImage, size); + } else { + console.log(`Importing avatar ${size}x${size} for ${url}`); + await collectAvatar(url, pathFinalImage, 64); + } } else { console.log(`Skipped import of avatar ${size}x${size} for ${url}`); } @@ -110,15 +128,43 @@ async function syncStaticAssets() { if (existsSync(resultingFilePath)) { console.log(`Skipped import of image ${assetName}`); } else { - console.log(`Importing image for ${assetName}`); mkdirSync(resultingFileDirectoryPath, { recursive: true }); - await collectAsset(assetName, assetHash, resultingFilePath); + if (IS_CLOUD_CLAUDE) { + console.log(`[prebuild] Cloud Claude Code detected, writing placeholder for asset ${assetName}`); + await writePlaceholderImage(resultingFilePath, 1); + } else { + console.log(`Importing image for ${assetName}`); + await collectAsset(assetName, assetHash, resultingFilePath); + } } }); await Promise.all(pendingImages); } syncStaticAssets(); +// Collecting SPONSORS SVG + +async function syncSponsors() { + const sponsorsPath = join(import.meta.dirname, '..', 'static', 'img', 'sponsors.svg'); + if (existsSync(sponsorsPath)) { + console.log('Skipped import of sponsors.svg'); + return; + } + mkdirSync(path.dirname(sponsorsPath), { recursive: true }); + if (IS_CLOUD_CLAUDE) { + console.log('[prebuild] Cloud Claude Code detected, writing placeholder sponsors.svg'); + await writeFile(sponsorsPath, ''); + return; + } + console.log('Importing sponsors.svg'); + const response = await fetch('https://raw.githubusercontent.com/dubzzz/sponsors-svg/main/sponsorkit/sponsors.svg'); + if (!response.ok) { + throw new Error(`Failed to fetch sponsors.svg: ${response.status}`); + } + await writeFile(sponsorsPath, Buffer.from(await response.arrayBuffer())); +} +syncSponsors(); + // Make sure we don't miss any static asset async function assessMissingSocialImage() { -- 2.51.2