ci(deploy): switch from Cloudflare Pages to GitHub Pages

Migrate build and deployment pipeline from Cloudflare Pages to GitHub Pages.
Adds production-ready deploy workflow in deploy-github-pages.yml with proper
artifact handling. Removes Cloudflare-specific tooling: wrangler config, _headers,
_redirects, and CSP hash injection scripts (no longer needed with GitHub Pages
static hosting). Updates package.json build scripts and all project documentation
to reflect new deployment target and simplified architecture.
This commit is contained in:
tiennm99 committed 2026-05-09 23:25:06 +07:00
1 parent a06cbd2675
commit 8fc3ec6ebd
20 files changed
+385 -301

No files matched your search

-49
View File
@@ -1,49 +0,0 @@
#!/usr/bin/env node
/**
* Postbuild step: replace `'unsafe-inline'` in build/_headers script-src
* with the SHA-256 hash(es) of every inline <script> in
* build/index.html. Hash changes per build are expected (the
* SvelteKit bootstrap embeds a timestamped registration call), so
* this script must run on every build.
*
* If no inline scripts are present (future SvelteKit could go
* src-only), the script removes `'unsafe-inline'` entirely so the
* tightest possible CSP ships.
*/
import { readFileSync, writeFileSync } from "node:fs";
import { createHash } from "node:crypto";
const HEADERS = "build/_headers";
const HTML = "build/index.html";
const MARKER = `script-src 'self' 'unsafe-inline'`;
const html = readFileSync(HTML, "utf8");
const inlineScripts = [
...html.matchAll(/<script(?![^>]*\bsrc=)[^>]*>([\s\S]*?)<\/script>/g),
];
const hashes = inlineScripts.map((m) => {
const body = m[1];
const digest = createHash("sha256").update(body, "utf8").digest("base64");
return `'sha256-${digest}'`;
});
const replacement =
hashes.length > 0
? `script-src 'self' ${hashes.join(" ")}`
: `script-src 'self'`;
const headers = readFileSync(HEADERS, "utf8");
if (!headers.includes(MARKER)) {
console.error(
`inject-csp-hashes: marker not found in ${HEADERS}.\nLooking for: ${MARKER}\n` +
`Either the previous build already replaced it (re-run \`npm run build\` from clean) ` +
`or static/_headers no longer contains the relaxed script-src directive.`,
);
process.exit(1);
}
writeFileSync(HEADERS, headers.replace(MARKER, replacement), "utf8");
console.log(
`inject-csp-hashes: replaced 'unsafe-inline' with ${hashes.length} hash(es) in ${HEADERS}.`,
);
@@ -1,66 +0,0 @@
#!/usr/bin/env node
/**
* Guard: count inline <script> tags in build/index.html. SvelteKit
* currently emits exactly one inline bootstrap block — the relaxation
* we ship in static/_headers (`script-src 'self' 'unsafe-inline'`)
* is calibrated to that. If a future SvelteKit upgrade adds another
* inline block, this guard fails CI so we either (a) hash the new
* block into CSP or (b) bump EXPECTED_INLINE intentionally.
*
* Inline = no `src=` attribute. Module/external scripts are excluded.
*/
import { readFileSync } from "node:fs";
const EXPECTED_INLINE = 1;
const HTML_PATH = "build/index.html";
const HEADERS_PATH = "build/_headers";
let html;
try {
html = readFileSync(HTML_PATH, "utf8");
} catch (e) {
console.error(`verify-build: cannot read ${HTML_PATH} — run \`npm run build\` first.`);
process.exit(2);
}
const inline = (html.match(/<script(?![^>]*\bsrc=)[^>]*>/g) || []).length;
if (inline > EXPECTED_INLINE) {
console.error(
`verify-build: found ${inline} inline <script> tags in ${HTML_PATH} (expected ${EXPECTED_INLINE}).\n` +
`If this is intentional, update EXPECTED_INLINE in scripts/verify-build-inline-scripts.mjs\n` +
`AND add the SHA-256 hash(es) of the new inline block(s) to static/_headers script-src.`,
);
process.exit(1);
}
if (inline < EXPECTED_INLINE) {
console.warn(
`verify-build: found ${inline} inline <script> tags but expected ${EXPECTED_INLINE}.\n` +
`If SvelteKit changed its bootstrap strategy, lower EXPECTED_INLINE and tighten CSP.`,
);
}
// Post-Phase 7: script-src must NOT contain 'unsafe-inline' anymore —
// `inject-csp-hashes` should have replaced it with sha256 hashes.
let headers;
try {
headers = readFileSync(HEADERS_PATH, "utf8");
} catch {
console.warn(`verify-build: ${HEADERS_PATH} not found — skipping CSP check.`);
process.exit(0);
}
const scriptSrcLine = headers
.split("\n")
.find((l) => /script-src\b/.test(l) && /Content-Security-Policy/i.test(l));
// Single-line policy: Content-Security-Policy: ... script-src 'self' …
if (scriptSrcLine && /script-src[^;]*'unsafe-inline'/.test(scriptSrcLine)) {
console.error(
`verify-build: ${HEADERS_PATH} script-src still contains 'unsafe-inline'. ` +
`inject-csp-hashes.mjs should have replaced it with SHA-256 hash(es).`,
);
process.exit(1);
}
console.log(`verify-build: ${inline} inline <script> tag(s), CSP hashed — OK.`);