mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
[ci] verify dist deploy artifact and force HTML cache purge
This commit is contained in:
1 parent
2e50c7a58f
commit
6fce210044
7 files changed
+66
-4
No files matched your search
@@ -58,6 +58,9 @@ jobs:
|
|||||||
- name: Build assets
|
- name: Build assets
|
||||||
run: npm run build
|
run: npm run build
|
||||||
|
|
||||||
|
- name: Verify deploy artifact markup and hashed references
|
||||||
|
run: npm run verify:dist:deploy-artifact
|
||||||
|
|
||||||
- name: Smoke check hashed dist asset references
|
- name: Smoke check hashed dist asset references
|
||||||
run: npm run smoke:dist-assets
|
run: npm run smoke:dist-assets
|
||||||
|
|
||||||
|
|||||||
@@ -10,4 +10,5 @@
|
|||||||
|
|
||||||
- Static CSS/JS asset freshness is now driven by build-time manifest rewriting (`dist/asset-manifest.json`) instead of runtime `ASSET_VERSION` query-string mutation.
|
- Static CSS/JS asset freshness is now driven by build-time manifest rewriting (`dist/asset-manifest.json`) instead of runtime `ASSET_VERSION` query-string mutation.
|
||||||
- Keep source HTML entry points (`index.html`, `embed.html`) pointing at logical asset paths (for example, `js/index.js` and `css/tailwind.generated.css`). The build pipeline rewrites those references to content-hashed filenames in `dist/`.
|
- Keep source HTML entry points (`index.html`, `embed.html`) pointing at logical asset paths (for example, `js/index.js` and `css/tailwind.generated.css`). The build pipeline rewrites those references to content-hashed filenames in `dist/`.
|
||||||
|
- Deploy pipelines should always purge HTML entrypoints (`/` + `/index.html` and `/embed.html`) at the edge after publish so clients do not keep serving stale source shells.
|
||||||
- App routing query parameters (such as `?v=` for video pointers) are unchanged and remain part of runtime URL behavior.
|
- App routing query parameters (such as `?v=` for video pointers) are unchanged and remain part of runtime URL behavior.
|
||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
publish = "dist"
|
publish = "dist"
|
||||||
|
|
||||||
[build.environment]
|
[build.environment]
|
||||||
NODE_ENV = "development"
|
NODE_ENV = "production"
|
||||||
|
|
||||||
[[headers]]
|
[[headers]]
|
||||||
for = "/*"
|
for = "/*"
|
||||||
|
|||||||
@@ -42,6 +42,7 @@
|
|||||||
"lint:sw-compat": "node scripts/check-sw-compat.mjs",
|
"lint:sw-compat": "node scripts/check-sw-compat.mjs",
|
||||||
"hash:dist": "node scripts/hash-dist.mjs --dist dist --output artifacts/dist-hashes-current.json",
|
"hash:dist": "node scripts/hash-dist.mjs --dist dist --output artifacts/dist-hashes-current.json",
|
||||||
"diff:dist-hashes": "node scripts/diff-dist-hashes.mjs",
|
"diff:dist-hashes": "node scripts/diff-dist-hashes.mjs",
|
||||||
|
"verify:dist:deploy-artifact": "node scripts/verify-dist-deploy-artifact.mjs",
|
||||||
"purge:cloudflare:changed": "node scripts/purge-cloudflare-changed.mjs",
|
"purge:cloudflare:changed": "node scripts/purge-cloudflare-changed.mjs",
|
||||||
"smoke:dist-assets": "node scripts/check-asset-references.mjs",
|
"smoke:dist-assets": "node scripts/check-asset-references.mjs",
|
||||||
"test:e2e:cache-upgrade": "npx playwright test tests/e2e/cache-upgrade-smoke.spec.ts --project=e2e"
|
"test:e2e:cache-upgrade": "npx playwright test tests/e2e/cache-upgrade-smoke.spec.ts --project=e2e"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
|
|
||||||
const ALWAYS_PUBLISH_PATHS = new Set(['index.html', 'embed.html', 'sw.min.js', 'site.webmanifest']);
|
const ALWAYS_PUBLISH_PATHS = new Set(['index.html', 'embed.html', 'sw.min.js', 'site.webmanifest']);
|
||||||
|
const ALWAYS_PURGE_HTML_PATHS = ['index.html', 'embed.html'];
|
||||||
const NON_ASSET_CHANGED_PATHS = new Set(['asset-manifest.json', '_headers', '_redirects']);
|
const NON_ASSET_CHANGED_PATHS = new Set(['asset-manifest.json', '_headers', '_redirects']);
|
||||||
|
|
||||||
function parseArgs(argv) {
|
function parseArgs(argv) {
|
||||||
@@ -82,8 +83,8 @@ function main() {
|
|||||||
const hashedModeFullyActive = unhashedAssetPaths.length === 0;
|
const hashedModeFullyActive = unhashedAssetPaths.length === 0;
|
||||||
|
|
||||||
const purgePaths = hashedModeFullyActive
|
const purgePaths = hashedModeFullyActive
|
||||||
? changedPaths.filter(isHtmlOrServiceWorkerPath)
|
? [...changedPaths.filter(isHtmlOrServiceWorkerPath), ...ALWAYS_PURGE_HTML_PATHS]
|
||||||
: changedPaths;
|
: [...changedPaths, ...ALWAYS_PURGE_HTML_PATHS];
|
||||||
|
|
||||||
const uniquePurgePaths = uniqueSorted(purgePaths);
|
const uniquePurgePaths = uniqueSorted(purgePaths);
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,13 @@ function readChangedPaths(args) {
|
|||||||
|
|
||||||
output.push(...args.changedPaths);
|
output.push(...args.changedPaths);
|
||||||
|
|
||||||
return [...new Set(output.map((value) => value.trim()).filter(Boolean))].sort((a, b) => a.localeCompare(b));
|
const normalized = output.map((value) => value.trim()).filter(Boolean);
|
||||||
|
|
||||||
|
if (normalized.includes('index.html')) {
|
||||||
|
normalized.push('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...new Set(normalized)].sort((a, b) => a.localeCompare(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
function toAbsoluteUrl(baseUrl, relativePath) {
|
function toAbsoluteUrl(baseUrl, relativePath) {
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
|
||||||
|
const DIST_DIR = 'dist';
|
||||||
|
const INDEX_PATH = path.join(DIST_DIR, 'index.html');
|
||||||
|
const MANIFEST_PATH = path.join(DIST_DIR, 'asset-manifest.json');
|
||||||
|
const VERSION_MARKUP_PATTERN = /seed\.\s*zap\.\s*subscribe\.\s*<\/h2>\s*<div[^>]*>\s*v:\s*[a-f0-9]{8}\s*•\s*\d{4}-\d{2}-\d{2}\s*<\/div>/i;
|
||||||
|
|
||||||
|
function readUtf8(filePath) {
|
||||||
|
if (!fs.existsSync(filePath)) {
|
||||||
|
throw new Error(`Missing required file: ${filePath}`);
|
||||||
|
}
|
||||||
|
return fs.readFileSync(filePath, 'utf8');
|
||||||
|
}
|
||||||
|
|
||||||
|
function assert(condition, message) {
|
||||||
|
if (!condition) {
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
const indexHtml = readUtf8(INDEX_PATH);
|
||||||
|
assert(
|
||||||
|
VERSION_MARKUP_PATTERN.test(indexHtml),
|
||||||
|
'dist/index.html is missing version markup under the slogan (expected: v: <8-char-hash> • <date>).'
|
||||||
|
);
|
||||||
|
|
||||||
|
const manifest = JSON.parse(readUtf8(MANIFEST_PATH));
|
||||||
|
const cssHashedPath = manifest['css/tailwind.generated.css'];
|
||||||
|
const jsHashedPath = manifest['js/index.js'];
|
||||||
|
|
||||||
|
assert(cssHashedPath, 'dist/asset-manifest.json is missing css/tailwind.generated.css.');
|
||||||
|
assert(jsHashedPath, 'dist/asset-manifest.json is missing js/index.js.');
|
||||||
|
|
||||||
|
assert(
|
||||||
|
indexHtml.includes(`href="${cssHashedPath}"`) || indexHtml.includes(`href='${cssHashedPath}'`),
|
||||||
|
`dist/index.html does not reference manifest CSS asset: ${cssHashedPath}`
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(
|
||||||
|
indexHtml.includes(`src="${jsHashedPath}"`) || indexHtml.includes(`src='${jsHashedPath}'`),
|
||||||
|
`dist/index.html does not reference manifest JS asset: ${jsHashedPath}`
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('Deployment artifact verification passed: version markup and hashed asset references are present in dist/index.html.');
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
Reference in new issue
Block a user