From 585aa9f6b24561fe45ea298787a291390ba8eea2 Mon Sep 17 00:00:00 2001 From: James Date: Fri, 10 Jul 2026 09:27:58 -0700 Subject: [PATCH] chore(repo): forbid tracked generated artifacts --- .github/workflows/regression.yml | 11 +++- lefthook.yml | 5 ++ package.json | 5 +- packages/producer/.DS_Store | Bin 8196 -> 0 bytes packages/producer/tests/.DS_Store | Bin 6148 -> 0 bytes .../producer/tests/style-1-prod/.DS_Store | Bin 6148 -> 0 bytes scripts/check-tracked-artifacts.mjs | 48 ++++++++++++++++++ scripts/check-tracked-artifacts.test.mjs | 32 ++++++++++++ 8 files changed, 98 insertions(+), 3 deletions(-) delete mode 100644 packages/producer/.DS_Store delete mode 100644 packages/producer/tests/.DS_Store delete mode 100644 packages/producer/tests/style-1-prod/.DS_Store create mode 100644 scripts/check-tracked-artifacts.mjs create mode 100644 scripts/check-tracked-artifacts.test.mjs diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml index 007eac30c7..6388c912d2 100644 --- a/.github/workflows/regression.yml +++ b/.github/workflows/regression.yml @@ -8,6 +8,12 @@ permissions: env: HYPERFRAMES_NO_TELEMETRY: "1" +# Graphite can update a branch and its PR base separately for the same head +# SHA. Keep only the newest expensive regression run for each PR/ref. +concurrency: + group: regression-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + on: pull_request: push: @@ -111,7 +117,10 @@ jobs: load: true tags: hyperframes-producer:test cache-from: type=gha,scope=regression-test-image - cache-to: type=gha,mode=max,scope=regression-test-image + # PR matrices can fan out across many stacked branches at once. Let + # them consume the shared cache, but keep a single writer on main so + # concurrent exports cannot exhaust the Actions cache service. + cache-to: ${{ github.event_name == 'push' && 'type=gha,mode=max,scope=regression-test-image' || '' }} - name: "Run regression shard: ${{ matrix.shard }}" run: | diff --git a/lefthook.yml b/lefthook.yml index ac248273b5..3477e0df44 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -38,6 +38,11 @@ pre-commit: # split paths containing spaces. It skips files under the limit, anything # routed through LFS, and registry/ assets. Tune via HF_MAX_NONLFS_KB. run: ./scripts/check-large-files.sh + tracked-artifacts: + # Keep ignored dependency trees and platform metadata out of commits. + # `git ls-files` observes the staged index, so staged removals pass and + # an accidental force-add fails before the commit is created. + run: bun run check:tracked-artifacts filesize: # Scoped to packages/studio — the 600 LOC limit is a studio architecture # standard enforced as part of the App.tsx decomposition work. Player and diff --git a/package.json b/package.json index 33b4b16ce2..5535bb035e 100644 --- a/package.json +++ b/package.json @@ -24,15 +24,16 @@ "changelog:weekly": "tsx scripts/changelog-weekly.ts", "sync-schemas": "tsx scripts/sync-schemas.ts", "sync-schemas:check": "tsx scripts/sync-schemas.ts --check", - "lint": "oxlint . && tsx scripts/lint-skills.ts", + "lint": "bun run check:tracked-artifacts && oxlint . && tsx scripts/lint-skills.ts", "lint:skills": "tsx scripts/lint-skills.ts", "lint:fix": "oxlint --fix .", + "check:tracked-artifacts": "node scripts/check-tracked-artifacts.mjs", "format": "oxfmt .", "test": "bun run --filter '*' test", "player:perf": "bun run --filter @hyperframes/player perf", "format:check": "oxfmt --check .", "knip": "knip", - "test:scripts": "node --import tsx --test scripts/validate-release-channel.test.mjs scripts/draft-changelog.test.ts scripts/set-version.test.ts scripts/release-prepare.test.ts scripts/cli-options.test.ts scripts/changelog-weekly.test.ts scripts/claude-plugin-compression.test.ts scripts/verify-packed-manifests.test.mjs", + "test:scripts": "node --import tsx --test scripts/check-tracked-artifacts.test.mjs scripts/validate-release-channel.test.mjs scripts/draft-changelog.test.ts scripts/set-version.test.ts scripts/release-prepare.test.ts scripts/cli-options.test.ts scripts/changelog-weekly.test.ts scripts/claude-plugin-compression.test.ts scripts/verify-packed-manifests.test.mjs", "test:skills": "node --test 'skills/**/*.test.mjs'", "generate:previews": "tsx scripts/generate-template-previews.ts", "generate:catalog-previews": "tsx scripts/generate-catalog-previews.ts", diff --git a/packages/producer/.DS_Store b/packages/producer/.DS_Store deleted file mode 100644 index 277ac7d4c49cf191654b67c4bfb299ba72de6859..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8196 zcmeHMU2GIp6u#fIzzh>$s09i#c58)VSwjm2ttf1_ZRDrrZ(I7q&$_!a&18U-n#{V~>KA=WX^ws~w_&|^-F%e_p6E7OU2NUDDb7u)#sBanqbCY||oO92) zx97}v?%gwGj3HStS{bWjjLFnF)l?d8(D*#>*EK1WR1*c+Gv;N-y|JwAX0OwY1W^Q{ z2t*NxA`nF&ioneg0lKq!lV`a1#cVW25r`siTSkDN4>9VTCPG>gG&nkF2uc8=yaWgu zJyRVJ*u+Ab2x&>sz?9Y$)dNCPge3-qInBqoI?+T(OM(h>24T(+?u@WPL9jd7#R7GP zgrLzFMIeg6^%3CdGmH7mWFC#z*6(MsW47h_EiLCERaMWLJx87+tMY-=h&!J0Gk(EK zX7c+u*XO!+w)~vQ?zPPEv{u{cI)27-%zR*A878UhAFv$59Z$Li$M6E%l$ekeSxIYk zlap=h*TrM4ZS7O>*kt?KmUygf-G-?tMPAgnX5*gp5qs2ej|(Rd9t3ROG^b8fdSrXW zPVq&eDpmIALfIo_uBy({2L^|R_w7$>TER2-n0Cguie^{FH@&pBz#|8@=3Hl2-Yk-1 z13A|&j5;*4Fqg4&1yVLUY&&H=CbV;T*S2@Lp5plEqk);9ac zEyEuf%6K``F)ZisFvZmItwWZXR~90!^f{SP^IT2c(q(rp*Y)Yzr2uS=iDm)1MnM=0V%(T$5qxb~98!L{>8yn% z**12Njj;)Kg1x}rU>~s4>_hfB`<9(&KeAuhZ|rw=nO$Ljp&D~hi{)5>Cal7}Sc`R7 zj}7QV4|=f!!^pwFAsmK{0*W|>2|SLc@HCF&1fIvscm=QFb)3XIco*;C6MTx#@CCla zdHjTn_!*aQ8CUS9q)0W=a!Hr&lA5GdQnR#L+9Yk3wn)3BVJR(*2oI!kvK^@OOqwA6 zn&X3#E&Ti^ow{+d)jzUj>$dGZH*K>0FrTN_9kq)VFNv*aUeor#!yQ*aC@@=w>9z0= z5MPle0pbHxOh>E8L$$u2Qo=koQ#xvMcAE)sV&fwz!_21w-T);2WaNJvoWBgHx<6aaiUlMVMxz0Qsp*@kd_1`3AO+IL%_fP*pBZ1=>BKkZE*kp1386jr2qf` diff --git a/packages/producer/tests/.DS_Store b/packages/producer/tests/.DS_Store deleted file mode 100644 index cb1dcac253eef04b98286c1c56733a81b71e37fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%}T>S5T0$TO{qc;3Oz1(t=QHgikDF93mDOZN^MNhV9b^#wMZ%CtS{t~_&m<+ zZbhj*DpqD-_S?+P?9Z35lK}wH9t9163II5$gt-zn-w2J9&PmQ%@`ysu(T8nlpblU- z=go${$N;^&HAoon)ZkPdP;D0h83LUT0#lA49C} z?O@64YO)2RT{MRe%{!}2F))pG(Sih~)xkgosK7*lF7m<7{{#Hn{6A@7N(HFEpDCdI z{ { + it("rejects node_modules at any directory depth", () => { + assert.equal(isForbiddenTrackedPath("node_modules/pkg/index.js"), true); + assert.equal(isForbiddenTrackedPath("packages/producer/node_modules/pkg"), true); + assert.equal(isForbiddenTrackedPath("packages\\producer\\node_modules\\pkg"), true); + }); + + it("rejects platform metadata by basename", () => { + assert.equal(isForbiddenTrackedPath(".DS_Store"), true); + assert.equal(isForbiddenTrackedPath("packages/producer/tests/.DS_Store"), true); + }); + + it("does not reject similarly named source paths", () => { + assert.equal(isForbiddenTrackedPath("docs/node_modules-policy.md"), false); + assert.equal(isForbiddenTrackedPath("packages/producer/src/DS_Store.ts"), false); + }); + + it("returns a deterministic sorted list", () => { + assert.deepEqual( + findForbiddenTrackedPaths([ + "packages/z/.DS_Store", + "packages/producer/src/index.ts", + "packages/a/node_modules/pkg", + ]), + ["packages/a/node_modules/pkg", "packages/z/.DS_Store"], + ); + }); +});