mirror of
https://github.com/aphrody-labs/bun-distribution.git
synced 2026-10-14 07:59:17 +00:00
quality: align libraries checks with shared process owner
This commit is contained in:
1 parent
090786e140
commit
89731c4f07
2 files changed
+20
-49
No files matched your search
@@ -1,14 +1,6 @@
|
||||
import { realpath } from "node:fs/promises";
|
||||
import { join, relative, sep } from "node:path";
|
||||
import {
|
||||
checkoutPath,
|
||||
findModule,
|
||||
hashContent,
|
||||
hashFile,
|
||||
hashStream,
|
||||
repositoryRoot,
|
||||
type Config,
|
||||
} from "./index.ts";
|
||||
import { checkoutPath, findModule, hashContent, hashFile, hashStream, repositoryRoot, type Config } from "./index.ts";
|
||||
|
||||
export interface AssetEntry {
|
||||
name: string;
|
||||
@@ -55,24 +47,17 @@ export interface AssetPlan {
|
||||
const maxAssetBytes = 128 * 1024 * 1024;
|
||||
const maxContentObjectBytes = 256 * 1024 * 1024;
|
||||
|
||||
export function validateAssetReference(
|
||||
value: unknown,
|
||||
baseUrl = "https://assets.aphrody.app",
|
||||
): AssetReference {
|
||||
if (!value || typeof value !== "object")
|
||||
throw new Error("An immutable shared asset reference is required.");
|
||||
export function validateAssetReference(value: unknown, baseUrl = "https://assets.aphrody.app"): AssetReference {
|
||||
if (!value || typeof value !== "object") throw new Error("An immutable shared asset reference is required.");
|
||||
const record = value as { reference?: unknown };
|
||||
const reference = (record.reference ?? value) as Partial<AssetReference>;
|
||||
if (!reference || typeof reference !== "object")
|
||||
throw new Error("An immutable shared asset reference is required.");
|
||||
if (!reference || typeof reference !== "object") throw new Error("An immutable shared asset reference is required.");
|
||||
if (
|
||||
typeof reference.sha256 !== "string" ||
|
||||
!/^[0-9a-f]{64}$/.test(reference.sha256) ||
|
||||
reference.url !== `${baseUrl}/manifests/sha256/${reference.sha256}.json`
|
||||
) {
|
||||
throw new Error(
|
||||
"Shared assets require the canonical custom domain and pinned manifest digest.",
|
||||
);
|
||||
throw new Error("Shared assets require the canonical custom domain and pinned manifest digest.");
|
||||
}
|
||||
return { sha256: reference.sha256, url: reference.url };
|
||||
}
|
||||
@@ -83,38 +68,26 @@ export async function planSharedAssets(config: Config, root = repositoryRoot): P
|
||||
const module = findModule(config, "craft-fonts");
|
||||
const directory = await realpath(checkoutPath(root, module.path));
|
||||
const manifestText = await Bun.file(join(directory, "fonts", "manifest.txt")).text();
|
||||
const rows = manifestText.split(/\r?\n/).filter((line) => line.trim() && !line.startsWith("#"));
|
||||
const rows = manifestText.split(/\r?\n/).filter(line => line.trim() && !line.startsWith("#"));
|
||||
if (rows.length > 128) throw new Error("Public font manifest exceeds its entry limit.");
|
||||
const entries: AssetEntry[] = [];
|
||||
const files = new Map<string, string>();
|
||||
let bytes = 0;
|
||||
const add = async (
|
||||
name: string,
|
||||
license: string,
|
||||
source: string,
|
||||
expected?: string,
|
||||
): Promise<AssetEntry> => {
|
||||
const add = async (name: string, license: string, source: string, expected?: string): Promise<AssetEntry> => {
|
||||
const path = await realpath(checkoutPath(directory, name));
|
||||
const local = relative(directory, path);
|
||||
if (local.startsWith(`..${sep}`) || local === "..")
|
||||
throw new Error("Public asset path leaves its owner.");
|
||||
if (local.startsWith(`..${sep}`) || local === "..") throw new Error("Public asset path leaves its owner.");
|
||||
const file = Bun.file(path);
|
||||
if (!file.size || file.size > maxAssetBytes)
|
||||
throw new Error(`Public asset size invalid: ${name}`);
|
||||
if (!file.size || file.size > maxAssetBytes) throw new Error(`Public asset size invalid: ${name}`);
|
||||
const sha256 = await hashFile(path, { maxBytes: maxAssetBytes });
|
||||
if (expected && sha256 !== expected)
|
||||
throw new Error(`Font differs from its owner manifest: ${name}`);
|
||||
if (expected && sha256 !== expected) throw new Error(`Font differs from its owner manifest: ${name}`);
|
||||
const key = `sha256/${sha256}`;
|
||||
if (!files.has(key)) {
|
||||
files.set(key, path);
|
||||
bytes += file.size;
|
||||
}
|
||||
if (bytes > 256 * 1024 * 1024) throw new Error("Public asset plan exceeds its size limit.");
|
||||
const type = name.endsWith(".ttf")
|
||||
? "font/ttf"
|
||||
: name.endsWith(".otf")
|
||||
? "font/otf"
|
||||
: "text/plain; charset=utf-8";
|
||||
const type = name.endsWith(".ttf") ? "font/ttf" : name.endsWith(".otf") ? "font/otf" : "text/plain; charset=utf-8";
|
||||
const entry: AssetEntry = { name, key, sha256, size: file.size, type, license, source };
|
||||
entries.push(entry);
|
||||
return entry;
|
||||
@@ -154,7 +127,7 @@ export async function publishSharedAssets(
|
||||
): Promise<{ uploaded: number; retained: number }> {
|
||||
let uploaded = 0;
|
||||
let retained = 0;
|
||||
const objects = new Map(plan.manifest.entries.map((entry) => [entry.key, entry]));
|
||||
const objects = new Map(plan.manifest.entries.map(entry => [entry.key, entry]));
|
||||
for (const [key, path] of plan.files) {
|
||||
const entry = objects.get(key)!;
|
||||
if ((await hashFile(path, { maxBytes: maxAssetBytes })) !== entry.sha256)
|
||||
@@ -175,12 +148,10 @@ export async function publishSharedAssets(
|
||||
}
|
||||
}
|
||||
const body = JSON.stringify(plan.manifest);
|
||||
if (hashContent(body) !== plan.reference.sha256)
|
||||
throw new Error("Shared asset manifest changed after planning.");
|
||||
if (hashContent(body) !== plan.reference.sha256) throw new Error("Shared asset manifest changed after planning.");
|
||||
const remote = client.file(`manifests/sha256/${plan.reference.sha256}.json`);
|
||||
if (await remote.exists()) {
|
||||
if (hashContent(await remote.text()) !== plan.reference.sha256)
|
||||
throw new Error("Immutable R2 manifest differs.");
|
||||
if (hashContent(await remote.text()) !== plan.reference.sha256) throw new Error("Immutable R2 manifest differs.");
|
||||
retained++;
|
||||
} else {
|
||||
await remote.write(body, { type: "application/json", retry: 1 });
|
||||
@@ -273,8 +244,7 @@ export async function publishContentObject(
|
||||
maxBytes: expected.bytes,
|
||||
expectedBytes: expected.bytes,
|
||||
});
|
||||
if (digest !== expected.sha256)
|
||||
throw new Error("Published R2 asset does not match its content address.");
|
||||
if (digest !== expected.sha256) throw new Error("Published R2 asset does not match its content address.");
|
||||
return {
|
||||
...expected,
|
||||
objectKey,
|
||||
|
||||
+5
-4
@@ -2,14 +2,15 @@
|
||||
import { resolve } from "node:path";
|
||||
import { runProcess } from "../packages/workspace/src/process.ts";
|
||||
|
||||
const checks = ["doctor", "typecheck", "lint", "format:check"];
|
||||
const root = resolve(import.meta.dir, "..");
|
||||
const checks = ["typecheck", "lint", "format:check"];
|
||||
for (const check of checks) {
|
||||
const child = await runProcess([process.execPath, "run", "--bun", check], resolve(import.meta.dir, ".."), {
|
||||
timeoutMs: 120_000,
|
||||
const result = await runProcess([process.execPath, "run", "--bun", check], root, {
|
||||
timeoutMs: 300_000,
|
||||
maxOutputBytes: 64 * 1024 * 1024,
|
||||
stdin: "inherit",
|
||||
stdout: "inherit",
|
||||
stderr: "inherit",
|
||||
});
|
||||
if (child.code !== 0) process.exit(child.code);
|
||||
if (result.code !== 0) process.exit(result.code);
|
||||
}
|
||||
Reference in new issue
Block a user