Merge branch 'feat/do-package-fixes' into feat/desktop-offline

This commit is contained in:
Linus Rath
2026-11-04 03:43:04 +01:00
2 changed files with 37 additions and 4 deletions
+31 -1
View File
@@ -1,6 +1,6 @@
// @vitest-environment node
import { afterEach, describe, expect, it } from 'vitest';
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
import { spawnSync } from 'node:child_process';
import os from 'node:os';
import path from 'node:path';
@@ -120,6 +120,36 @@ describe('standalone guard check', () => {
expect(findForbidden(dir)).toEqual(['worktrees/webmail/data/']);
});
it('goes by the name in any case, as a case-insensitive disk and Docker context would', () => {
const dir = tempDir();
put(dir, 'server.js');
put(dir, '.ENV', 'SECRET=1');
put(dir, 'Plugin-Signing.KEY', 'SECRET=1');
put(dir, 'Data/admin.json');
put(dir, 'LOCAL-DATA/a');
put(dir, 'Repos/a');
expect(findForbidden(dir)).toEqual(['.ENV', 'Data/', 'LOCAL-DATA/', 'Plugin-Signing.KEY', 'Repos/']);
});
it('refuses a link: it would ship whatever it points at', () => {
const dir = tempDir();
const outside = tempDir();
put(outside, 'admin.json');
put(dir, 'server.js');
symlinkSync(outside, path.join(dir, 'state'), 'junction');
expect(findForbidden(dir)).toEqual(['state (link)']);
});
it('leaves the links of .next and node_modules alone: Turbopack makes those on purpose', () => {
const dir = tempDir();
const outside = tempDir();
put(dir, 'node_modules/a/index.js');
mkdirSync(path.join(dir, '.next', 'node_modules'), { recursive: true });
symlinkSync(path.join(dir, 'node_modules', 'a'), path.join(dir, '.next', 'node_modules', 'a-1234'), 'junction');
symlinkSync(outside, path.join(dir, 'node_modules', 'b'), 'junction');
expect(findForbidden(dir)).toEqual([]);
});
it('removes the .env files Next copies next to server.js, then passes', () => {
const dir = tempDir();
put(dir, 'worktrees/webmail/server.js');
+6 -3
View File
@@ -25,7 +25,7 @@ const TRACE_ALLOWED = new Set(['node_modules', '.next', 'public', 'locales', 'pa
/** Never in a standalone tree, wherever it sits outside node_modules. */
const FORBIDDEN_DIRS = new Set(['data', 'local-data', 'repos']);
const FORBIDDEN_FILE = /^(\.env.*|plugin-signing\.key)$/;
const FORBIDDEN_FILE = /^(\.env.*|plugin-signing\.key)$/i;
function toPosix(p) {
return p.split(path.sep).join('/');
@@ -106,10 +106,13 @@ export function findForbidden(standaloneDir) {
const visit = (dir) => {
for (const entry of readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) {
if (entry.isSymbolicLink()) {
// Not .next's or a package's, which aren't visited: Turbopack links those on purpose.
if (entry.name !== 'node_modules' && entry.name !== '.next') found.push(`${toPosix(path.relative(standaloneDir, full))} (link)`);
} else if (entry.isDirectory()) {
// Packages may hold anything, and .next/server has routes named after data.
if (entry.name === 'node_modules' || entry.name === '.next') continue;
if (FORBIDDEN_DIRS.has(entry.name)) found.push(toPosix(path.relative(standaloneDir, full)) + '/');
if (FORBIDDEN_DIRS.has(entry.name.toLowerCase())) found.push(toPosix(path.relative(standaloneDir, full)) + '/');
else visit(full);
} else if (FORBIDDEN_FILE.test(entry.name)) {
found.push(toPosix(path.relative(standaloneDir, full)));