From e1f92075044f52f782fd62a2b68c970ebf70ba6d Mon Sep 17 00:00:00 2001 From: Montasim Date: Mon, 10 Aug 2026 08:00:13 +0600 Subject: [PATCH] fix(web): skip Netlify for extension-only changes --- apps/web/netlify.toml | 1 + apps/web/package.json | 3 +- apps/web/tests/netlify-build-filter.test.mjs | 90 ++++++++++++++++++++ 3 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 apps/web/tests/netlify-build-filter.test.mjs diff --git a/apps/web/netlify.toml b/apps/web/netlify.toml index dc43d86..f69399d 100644 --- a/apps/web/netlify.toml +++ b/apps/web/netlify.toml @@ -1,6 +1,7 @@ [build] command = "pnpm --filter @vidquery/web build" publish = "/apps/web/dist/client" + ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF -- apps/web package.json pnpm-lock.yaml pnpm-workspace.yaml" [build.environment] NODE_VERSION = "20.19.3" diff --git a/apps/web/package.json b/apps/web/package.json index 442ab53..38950d9 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -13,7 +13,8 @@ "lint": "eslint", "format": "prettier --write . && eslint --fix", "typecheck": "tsc --noEmit", - "check": "tsr generate && prettier --check . && eslint && tsc --noEmit && vite build" + "test:deploy-filter": "node --test tests/netlify-build-filter.test.mjs", + "check": "tsr generate && prettier --check . && eslint && tsc --noEmit && node --test tests/netlify-build-filter.test.mjs && vite build" }, "dependencies": { "@fontsource-variable/geist": "^5.3.0", diff --git a/apps/web/tests/netlify-build-filter.test.mjs b/apps/web/tests/netlify-build-filter.test.mjs new file mode 100644 index 0000000..8e74134 --- /dev/null +++ b/apps/web/tests/netlify-build-filter.test.mjs @@ -0,0 +1,90 @@ +import assert from 'node:assert/strict' +import { execFileSync, spawnSync } from 'node:child_process' +import { + mkdtempSync, + mkdirSync, + readFileSync, + rmSync, + writeFileSync, +} from 'node:fs' +import { tmpdir } from 'node:os' +import { dirname, join, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' +import test from 'node:test' + +const webRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..') + +function git(cwd, ...args) { + return execFileSync('git', args, { cwd, encoding: 'utf8' }).trim() +} + +function writeFixture(root, path, value) { + const destination = join(root, path) + mkdirSync(dirname(destination), { recursive: true }) + writeFileSync(destination, value) +} + +function runIgnoreCommand(command, cwd, cachedCommit, commit) { + return spawnSync('bash', ['-c', command], { + cwd, + env: { + ...process.env, + CACHED_COMMIT_REF: cachedCommit, + COMMIT_REF: commit, + }, + }) +} + +test('Netlify skips extension-only changes and builds web-related changes', () => { + const config = readFileSync(join(webRoot, 'netlify.toml'), 'utf8') + const ignoreCommand = config.match(/^\s*ignore\s*=\s*"([^"]+)"/m)?.[1] + assert.ok(ignoreCommand, 'apps/web/netlify.toml must define build.ignore') + + const fixture = mkdtempSync(join(tmpdir(), 'netlify-build-filter-')) + try { + git(fixture, 'init', '--quiet') + git(fixture, 'config', 'user.email', 'test@example.com') + git(fixture, 'config', 'user.name', 'Netlify filter test') + writeFixture(fixture, 'apps/web/index.ts', 'web v1\n') + writeFixture(fixture, 'apps/extension/index.ts', 'extension v1\n') + writeFixture(fixture, 'package.json', '{}\n') + writeFixture(fixture, 'pnpm-lock.yaml', 'lockfileVersion: 9\n') + writeFixture(fixture, 'pnpm-workspace.yaml', 'packages: []\n') + git(fixture, 'add', '.') + git(fixture, 'commit', '--quiet', '-m', 'initial') + const initial = git(fixture, 'rev-parse', 'HEAD') + + writeFixture(fixture, 'apps/extension/index.ts', 'extension v2\n') + git(fixture, 'add', '.') + git(fixture, 'commit', '--quiet', '-m', 'extension only') + const extensionOnly = git(fixture, 'rev-parse', 'HEAD') + assert.equal( + runIgnoreCommand(ignoreCommand, fixture, initial, extensionOnly).status, + 0, + 'an extension-only change should stop the Netlify build', + ) + + writeFixture(fixture, 'apps/web/index.ts', 'web v2\n') + git(fixture, 'add', '.') + git(fixture, 'commit', '--quiet', '-m', 'web change') + const webChange = git(fixture, 'rev-parse', 'HEAD') + assert.equal( + runIgnoreCommand(ignoreCommand, fixture, extensionOnly, webChange).status, + 1, + 'a landing-page change should continue the Netlify build', + ) + + writeFixture(fixture, 'package.json', '{"private":true}\n') + git(fixture, 'add', '.') + git(fixture, 'commit', '--quiet', '-m', 'workspace change') + const workspaceChange = git(fixture, 'rev-parse', 'HEAD') + assert.equal( + runIgnoreCommand(ignoreCommand, fixture, webChange, workspaceChange) + .status, + 1, + 'a shared workspace change should continue the Netlify build', + ) + } finally { + rmSync(fixture, { recursive: true, force: true }) + } +})