Skip to content

Commit dd9fe8d

Browse files
committed
fix(ci): validate generated size history branches
1 parent 3e08ad1 commit dd9fe8d

3 files changed

Lines changed: 97 additions & 1 deletion

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---

scripts/changeset-ci.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,52 @@ import { PUBLISHABLE_PACKAGE_ORDER } from "./workspace-tooling.js";
44

55
const STABLE_VERSION = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
66
const REPOSITORY_ROOT = resolve(import.meta.dir, "..");
7+
const PACKAGE_SIZE_HISTORY_PREFIX = "automation/package-size-history-";
8+
const REQUIRED_PACKAGE_SIZE_HISTORY_FILES = new Set([
9+
"docs/src/content/docs/guides/performance-resources.md",
10+
"scripts/size-history.json",
11+
]);
12+
const ALLOWED_PACKAGE_SIZE_HISTORY_FILES = new Set([
13+
...REQUIRED_PACKAGE_SIZE_HISTORY_FILES,
14+
"docs/src/generated/docs-contract.json",
15+
]);
716

817
export function releaseVersionFromHeadRef(headRef: string | undefined): string | undefined {
918
return headRef !== undefined && STABLE_VERSION.test(headRef) ? headRef : undefined;
1019
}
20+
export function packageSizeHistoryVersionFromHeadRef(
21+
headRef: string | undefined,
22+
): string | undefined {
23+
if (headRef === undefined || !headRef.startsWith(PACKAGE_SIZE_HISTORY_PREFIX)) return undefined;
24+
const version = headRef.slice(PACKAGE_SIZE_HISTORY_PREFIX.length);
25+
return STABLE_VERSION.test(version) ? version : undefined;
26+
}
27+
export function isGeneratedPackageSizeHistoryChange(paths: readonly string[]): boolean {
28+
const uniquePaths = new Set(paths);
29+
return (
30+
uniquePaths.size === paths.length &&
31+
[...REQUIRED_PACKAGE_SIZE_HISTORY_FILES].every((path) => uniquePaths.has(path)) &&
32+
paths.every((path) => ALLOWED_PACKAGE_SIZE_HISTORY_FILES.has(path))
33+
);
34+
}
35+
36+
async function changedFilesSinceDevelop(): Promise<string[]> {
37+
const child = Bun.spawn(["git", "diff", "--name-only", "origin/develop...HEAD"], {
38+
cwd: REPOSITORY_ROOT,
39+
stdout: "pipe",
40+
stderr: "pipe",
41+
});
42+
const [stdout, stderr, exitCode] = await Promise.all([
43+
new Response(child.stdout).text(),
44+
new Response(child.stderr).text(),
45+
child.exited,
46+
]);
47+
if (exitCode !== 0) throw new Error(`Unable to inspect package size history diff: ${stderr}`);
48+
return stdout
49+
.split("\n")
50+
.map((path) => path.trim())
51+
.filter((path) => path.length > 0);
52+
}
1153

1254
export function validateReleasePackageVersions(version: string, root = REPOSITORY_ROOT): void {
1355
for (const name of PUBLISHABLE_PACKAGE_ORDER) {
@@ -30,6 +72,19 @@ async function main(): Promise<void> {
3072
console.log(`Release package versions match branch ${releaseVersion}`);
3173
return;
3274
}
75+
const packageSizeHistoryVersion = packageSizeHistoryVersionFromHeadRef(
76+
process.env.GITHUB_HEAD_REF,
77+
);
78+
if (packageSizeHistoryVersion !== undefined) {
79+
validateReleasePackageVersions(packageSizeHistoryVersion);
80+
if (isGeneratedPackageSizeHistoryChange(await changedFilesSinceDevelop())) {
81+
console.log(`Package size history versions match branch ${packageSizeHistoryVersion}`);
82+
return;
83+
}
84+
console.log(
85+
"Package size history branch contains non-generated changes; requiring a changeset",
86+
);
87+
}
3388
const child = Bun.spawn(["bun", "run", "changeset:status", "--", "--since=origin/develop"], {
3489
cwd: REPOSITORY_ROOT,
3590
stdin: "inherit",

scripts/workspace-tooling.test.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ import { afterEach, describe, expect, it } from "bun:test";
22
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
33
import { tmpdir } from "node:os";
44
import { join, resolve } from "node:path";
5-
import { releaseVersionFromHeadRef, validateReleasePackageVersions } from "./changeset-ci.js";
5+
import {
6+
isGeneratedPackageSizeHistoryChange,
7+
packageSizeHistoryVersionFromHeadRef,
8+
releaseVersionFromHeadRef,
9+
validateReleasePackageVersions,
10+
} from "./changeset-ci.js";
611
import {
712
assertUniqueOrderedNodes,
813
PACKAGE_BUILD_NODES,
@@ -246,6 +251,40 @@ describe("changeset workspace contract", () => {
246251
it("runs the release-aware Changesets status command", () => {
247252
expect(releaseVersionFromHeadRef("0.3.1")).toBe("0.3.1");
248253
expect(releaseVersionFromHeadRef("feature/docs")).toBeUndefined();
254+
expect(packageSizeHistoryVersionFromHeadRef("automation/package-size-history-0.3.1")).toBe(
255+
"0.3.1",
256+
);
257+
expect(packageSizeHistoryVersionFromHeadRef("automation/package-size-history-next")).toBe(
258+
undefined,
259+
);
260+
expect(
261+
isGeneratedPackageSizeHistoryChange([
262+
"docs/src/content/docs/guides/performance-resources.md",
263+
"scripts/size-history.json",
264+
]),
265+
).toBeTrue();
266+
expect(
267+
isGeneratedPackageSizeHistoryChange([
268+
"docs/src/content/docs/guides/performance-resources.md",
269+
"docs/src/generated/docs-contract.json",
270+
"scripts/size-history.json",
271+
]),
272+
).toBeTrue();
273+
expect(
274+
isGeneratedPackageSizeHistoryChange([
275+
"docs/src/content/docs/guides/performance-resources.md",
276+
"scripts/changeset-ci.ts",
277+
"scripts/size-history.json",
278+
]),
279+
).toBeFalse();
280+
expect(isGeneratedPackageSizeHistoryChange(["scripts/size-history.json"])).toBeFalse();
281+
expect(
282+
isGeneratedPackageSizeHistoryChange([
283+
"docs/src/content/docs/guides/performance-resources.md",
284+
"scripts/size-history.json",
285+
"scripts/size-history.json",
286+
]),
287+
).toBeFalse();
249288
expect(() => validateReleasePackageVersions("0.3.1")).not.toThrow();
250289
expect(() => validateReleasePackageVersions("0.3.2")).toThrow(
251290
"Release branch 0.3.2 requires @sheetwrite/wasm@0.3.2",

0 commit comments

Comments
 (0)