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
10 changes: 3 additions & 7 deletions registry/blocks/gooey-split/gooey-split.html
Original file line number Diff line number Diff line change
Expand Up @@ -280,13 +280,9 @@
var copies = samples + 1;
var alpha = String(1 / samples);

// A copy is styled by nothing that selected the original: dropping the id also
// drops every `#id` rule that gave it size, colour and font. So each copy carries
// its own resolved style inline. Chrome returns "" for a computed style's cssText,
// so the declarations are enumerated rather than taken wholesale.
// Visibility is not frozen into a copy: its root takes the element's current one from
// the group each frame, and a descendant keeps its own only where it differs from its
// parent's. A target hidden at attach (autoAlpha) would otherwise never smear.
// A copy loses the `#id` rules that styled the original, so it carries its resolved style inline
// (enumerated: Chrome returns "" for a computed cssText). Visibility is not frozen: the root
// follows the group each frame, so a target hidden at attach still smears.
function resolvedStyles(source, out, parentVisibility) {
var cs = getComputedStyle(source);
var text = "";
Expand Down
10 changes: 3 additions & 7 deletions registry/components/motion-blur/demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -402,13 +402,9 @@
var copies = samples + 1;
var alpha = String(1 / samples);

// A copy is styled by nothing that selected the original: dropping the id also
// drops every `#id` rule that gave it size, colour and font. So each copy carries
// its own resolved style inline. Chrome returns "" for a computed style's cssText,
// so the declarations are enumerated rather than taken wholesale.
// Visibility is not frozen into a copy: its root takes the element's current one from
// the group each frame, and a descendant keeps its own only where it differs from its
// parent's. A target hidden at attach (autoAlpha) would otherwise never smear.
// A copy loses the `#id` rules that styled the original, so it carries its resolved style inline
// (enumerated: Chrome returns "" for a computed cssText). Visibility is not frozen: the root
// follows the group each frame, so a target hidden at attach still smears.
function resolvedStyles(source, out, parentVisibility) {
var cs = getComputedStyle(source);
var text = "";
Expand Down
10 changes: 3 additions & 7 deletions registry/components/motion-blur/motion-blur.html
Original file line number Diff line number Diff line change
Expand Up @@ -271,13 +271,9 @@
var copies = samples + 1;
var alpha = String(1 / samples);

// A copy is styled by nothing that selected the original: dropping the id also
// drops every `#id` rule that gave it size, colour and font. So each copy carries
// its own resolved style inline. Chrome returns "" for a computed style's cssText,
// so the declarations are enumerated rather than taken wholesale.
// Visibility is not frozen into a copy: its root takes the element's current one from
// the group each frame, and a descendant keeps its own only where it differs from its
// parent's. A target hidden at attach (autoAlpha) would otherwise never smear.
// A copy loses the `#id` rules that styled the original, so it carries its resolved style inline
// (enumerated: Chrome returns "" for a computed cssText). Visibility is not frozen: the root
// follows the group each frame, so a target hidden at attach still smears.
function resolvedStyles(source, out, parentVisibility) {
var cs = getComputedStyle(source);
var text = "";
Expand Down
10 changes: 3 additions & 7 deletions registry/components/shutter-slam/demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -232,13 +232,9 @@
var copies = samples + 1;
var alpha = String(1 / samples);

// A copy is styled by nothing that selected the original: dropping the id also
// drops every `#id` rule that gave it size, colour and font. So each copy carries
// its own resolved style inline. Chrome returns "" for a computed style's cssText,
// so the declarations are enumerated rather than taken wholesale.
// Visibility is not frozen into a copy: its root takes the element's current one from
// the group each frame, and a descendant keeps its own only where it differs from its
// parent's. A target hidden at attach (autoAlpha) would otherwise never smear.
// A copy loses the `#id` rules that styled the original, so it carries its resolved style inline
// (enumerated: Chrome returns "" for a computed cssText). Visibility is not frozen: the root
// follows the group each frame, so a target hidden at attach still smears.
function resolvedStyles(source, out, parentVisibility) {
var cs = getComputedStyle(source);
var text = "";
Expand Down
10 changes: 3 additions & 7 deletions registry/components/shutter-slam/shutter-slam.html
Original file line number Diff line number Diff line change
Expand Up @@ -266,13 +266,9 @@
var copies = samples + 1;
var alpha = String(1 / samples);

// A copy is styled by nothing that selected the original: dropping the id also
// drops every `#id` rule that gave it size, colour and font. So each copy carries
// its own resolved style inline. Chrome returns "" for a computed style's cssText,
// so the declarations are enumerated rather than taken wholesale.
// Visibility is not frozen into a copy: its root takes the element's current one from
// the group each frame, and a descendant keeps its own only where it differs from its
// parent's. A target hidden at attach (autoAlpha) would otherwise never smear.
// A copy loses the `#id` rules that styled the original, so it carries its resolved style inline
// (enumerated: Chrome returns "" for a computed cssText). Visibility is not frozen: the root
// follows the group each frame, so a target hidden at attach still smears.
function resolvedStyles(source, out, parentVisibility) {
var cs = getComputedStyle(source);
var text = "";
Expand Down
27 changes: 1 addition & 26 deletions scripts/catalog-drift.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,11 @@ import { join, relative, resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { generateCatalog } from "./generate-catalog.ts";
import { GENERATED_CATALOG_PATHS } from "./catalog-generated-paths.mjs";
import { VECTORS, vectorsAgree } from "./catalog-vectors.mjs";
import { runAsCommand } from "./entrypoint.ts";

const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const MAX_LISTED = 40;
// The embedding model's float output differs slightly between CPUs, so vectors are compared by direction, not bytes.
const VECTORS = /-vectors\.bin$/;
const MIN_ROW_COSINE = 0.99;

function filesUnder(root: string): string[] {
return readdirSync(root, { recursive: true, withFileTypes: true })
Expand Down Expand Up @@ -47,29 +45,6 @@ function sameContent(generatedRoot: string, committedRoot: string, file: string)
return vectorsAgree(generated, committed, dimensions);
}

/** Same row count, and every row of Float32 vectors points the same way within MIN_ROW_COSINE. */
export function vectorsAgree(a: Buffer, b: Buffer, dimensions: number): boolean {
const rowBytes = 4 * dimensions;
if (a.length !== b.length || a.length % rowBytes !== 0) return false;
return Array.from({ length: a.length / rowBytes }, (_, row) =>
rowCosine(a, b, row * rowBytes, (row + 1) * rowBytes),
).every((cosine) => cosine >= MIN_ROW_COSINE);
}

function rowCosine(a: Buffer, b: Buffer, start: number, end: number): number {
let dot = 0;
let normA = 0;
let normB = 0;
for (let i = start; i < end; i += 4) {
const x = a.readFloatLE(i);
const y = b.readFloatLE(i);
dot += x * y;
normA += x * x;
normB += y * y;
}
return dot / Math.sqrt(normA * normB);
}

async function main(): Promise<void> {
await generateCatalog();
for (const script of [
Expand Down
129 changes: 113 additions & 16 deletions scripts/catalog-publication.test.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import assert from "node:assert/strict";
import { execFileSync, spawnSync } from "node:child_process";
import { mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
import {
existsSync,
mkdirSync,
mkdtempSync,
readFileSync,
rmSync,
symlinkSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";
import { test } from "node:test";
Expand Down Expand Up @@ -93,19 +101,10 @@ function publicationFixture(t, changed, apiEnv = {}) {
mkdirSync(dirname(join(root, target)), { recursive: true });
writeFileSync(join(root, target), "published");
}
git("add", ".");
git(
"-c",
"user.name=Test",
"-c",
"user.email=test@example.invalid",
"-c",
"commit.gpgsign=false",
"commit",
"-qm",
"fixture",
);
const base = git("rev-parse", "HEAD");
const base = commitPaths(git, "fixture", ["."]);
git("init", "-q", "--bare", "origin.git");
git("remote", "add", "origin", join(root, "origin.git"));
git("push", "-q", "origin", "HEAD:refs/heads/bot/catalog-publish");
if (changed) writeFileSync(join(root, "registry/registry.json"), "new publication");
const executable = join(root, "gh");
writeFileSync(
Expand All @@ -127,7 +126,6 @@ else if (endpoint.endsWith("/dispatches") && process.env.DISPATCH_FAIL) process.
else if (endpoint.endsWith("/ref/heads/main")) console.log(process.env.BASE);
else if (endpoint.includes("/matching-refs/")) console.log("refs/heads/bot/catalog-publish");
else if (endpoint.includes("/ref/heads/bot/catalog-publish")) console.log("a".repeat(40));
else if (endpoint.includes("/commits/")) console.log("b".repeat(40));
else if (endpoint.includes("/pulls?")) console.log("42");
`,
{ mode: 0o755 },
Expand All @@ -153,7 +151,7 @@ catch (error) {
env: {
...process.env,
PATH: `${root}:${process.env.PATH}`,
BASE: base,
BASE: git("rev-parse", "HEAD"),
API_CALLS: calls,
FIXTURE_ROOT: root,
GITHUB_REPOSITORY: "test/catalog",
Expand All @@ -167,6 +165,7 @@ catch (error) {
return {
root,
base,
git,
run,
calls: () =>
readFileSync(calls, "utf8")
Expand Down Expand Up @@ -328,3 +327,101 @@ test("regression accepts completed shards after successful change detection", ()
const result = runSummary("success", "true", "success");
assert.equal(result.status, 0, result.stdout + result.stderr);
});

function commitPaths(git, message, paths) {
git("add", "-A", "--", ...paths);
git(
"-c",
"user.name=Test",
"-c",
"user.email=test@example.invalid",
"-c",
"commit.gpgsign=false",
"commit",
"-qm",
message,
);
return git("rev-parse", "HEAD");
}

const VECTOR_META = "registry/catalog-artifact/local-vectors.json";
const VECTOR_BIN = "registry/catalog-artifact/local-vectors.bin";
// Two rows of two dimensions.
const vectors = (...values) => Buffer.from(new Float32Array(values).buffer);

// Publishes the working tree's files at `paths` and leaves them in place, as the next run would regenerate them.
function publish(fixture, paths = GENERATED_CATALOG_PATHS) {
commitPaths(fixture.git, "publication", paths);
fixture.git("push", "-q", "-f", "origin", "HEAD:bot/catalog-publish");
fixture.git("reset", "-q", "HEAD~");
}

function commitToMain(fixture, path, contents) {
const kept = existsSync(join(fixture.root, path))
? readFileSync(join(fixture.root, path))
: undefined;
writeFileSync(join(fixture.root, path), contents);
commitPaths(fixture.git, "main push", [path]);
if (kept !== undefined) writeFileSync(join(fixture.root, path), kept);
}

const rewrites = (fixture) =>
fixture
.calls()
.some((call) => call.endpoint === "graphql" || call.endpoint.includes("/refs/heads/bot/"));

function vectorFixture(t) {
const fixture = publicationFixture(t, true, { PUBLISH_SUCCESS: "1" });
writeFileSync(join(fixture.root, VECTOR_META), JSON.stringify({ dimensions: 2 }));
writeFileSync(join(fixture.root, VECTOR_BIN), vectors(1, 0, 0, 1));
return fixture;
}

test("an unrelated main push leaves the approved publication alone", (t) => {
const fixture = publicationFixture(t, true, { PUBLISH_SUCCESS: "1" });
publish(fixture);
commitToMain(fixture, "notes.txt", "unrelated");
fixture.run();
assert.equal(rewrites(fixture), false);
});

test("regenerated vectors replace the publication only when they point elsewhere", (t) => {
for (const [regenerated, rewritten] of [
[vectors(1, 0.001, 0.001, 1), false],
[vectors(1, 0, 1, 0), true],
]) {
const fixture = vectorFixture(t);
publish(fixture);
commitToMain(fixture, "notes.txt", "unrelated");
writeFileSync(join(fixture.root, VECTOR_BIN), regenerated);
fixture.run();
assert.equal(rewrites(fixture), rewritten);
}
});

test("vectors the publication lacks replace it", (t) => {
const fixture = vectorFixture(t);
rmSync(join(fixture.root, VECTOR_BIN));
publish(fixture);
commitToMain(fixture, "notes.txt", "unrelated");
writeFileSync(join(fixture.root, VECTOR_BIN), vectors(1, 0, 0, 1));
fixture.run();
assert.equal(rewrites(fixture), true);
});

test("a main commit to a generated file replaces the publication", (t) => {
const fixture = publicationFixture(t, true, { PUBLISH_SUCCESS: "1" });
publish(fixture);
commitToMain(fixture, "registry/registry.json", "edited on main");
fixture.run();
assert.equal(rewrites(fixture), true);
});

test("a publication carrying anything but generated files is replaced", (t) => {
const fixture = publicationFixture(t, true, { PUBLISH_SUCCESS: "1" });
writeFileSync(join(fixture.root, "extra.sh"), "not generated");
publish(fixture, [...GENERATED_CATALOG_PATHS, "extra.sh"]);
commitToMain(fixture, "notes.txt", "unrelated");
fixture.run();
assert.equal(rewrites(fixture), true);
});
26 changes: 26 additions & 0 deletions scripts/catalog-vectors.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// The embedding model's float output differs slightly between CPUs, so vectors are compared by direction, not bytes.
export const VECTORS = /-vectors\.bin$/;
const MIN_ROW_COSINE = 0.99;

/** Same row count, and every row of Float32 vectors points the same way within MIN_ROW_COSINE. */
export function vectorsAgree(a, b, dimensions) {
const rowBytes = 4 * dimensions;
if (a.length !== b.length || a.length % rowBytes !== 0) return false;
return Array.from({ length: a.length / rowBytes }, (_, row) =>
rowCosine(a, b, row * rowBytes, (row + 1) * rowBytes),
).every((cosine) => cosine >= MIN_ROW_COSINE);
}

function rowCosine(a, b, start, end) {
let dot = 0;
let normA = 0;
let normB = 0;
for (let i = start; i < end; i += 4) {
const x = a.readFloatLE(i);
const y = b.readFloatLE(i);
dot += x * y;
normA += x * x;
normB += y * y;
}
return dot / Math.sqrt(normA * normB);
}
Loading
Loading