From 715fbc01ff1ae03cf27b6b1d155239bfe23cc4ea Mon Sep 17 00:00:00 2001 From: MongLong0214 Date: Sun, 16 Aug 2026 19:15:24 +0900 Subject: [PATCH] Assert the PowerShell floor install.ps1 already documents install.ps1 says at line 44 that Windows PowerShell 5.1 and 7+ both run it and that nothing uses 7-only syntax. That is true today -- no null-coalescing, no ternary, no pipeline chains, no -Parallel -- and nothing enforces it. Which is the shape found repeatedly in this repository this week: the intent is written where a reader looks and the enforcement is somewhere else, or nowhere. The failure it protects against is worse than an ordinary one. PowerShell parses the whole file before executing any of it, so a single `??` fails before the installer's own diagnostics can run. A 5.1 user would not see "Node.js 22.23.2 or newer is required" or "Nothing was installed" -- they would see a syntax error from a file they did not write, about a version nobody told them they needed. CI does run install.ps1 under both powershell.exe and pwsh, and that proves it works on a day someone maintains those jobs. This proves it on the day someone writes `??`. Comments are excluded from the scan so the file can keep naming these constructs while explaining why it avoids them -- which is exactly what line 45 does. Verified by injecting each construct: three injections, three failures, and the restored file green. Limit: a documented floor is enforced, or it is a preference Blast: local Undo: easy Certainty: firm Provenance: authored Record-Id: r-703ps51 --- test/install-ps1-language-floor.test.ts | 64 +++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 test/install-ps1-language-floor.test.ts diff --git a/test/install-ps1-language-floor.test.ts b/test/install-ps1-language-floor.test.ts new file mode 100644 index 00000000..bcbb3541 --- /dev/null +++ b/test/install-ps1-language-floor.test.ts @@ -0,0 +1,64 @@ +/** + * #703: `install.ps1` runs on PowerShell 5.1 by accident, not by assertion. + * + * It works there today — no PS7-only syntax appears in it, and CI runs it under + * both `powershell.exe` (5.1) and `pwsh` (7). But nothing states the floor, so + * nothing stops it moving. + * + * The failure that would follow is worse than a normal one. A parse error + * happens before any statement executes, so the installer's own diagnostics — + * "Node.js 22.23.2 or newer is required", "Nothing was installed" — never get a + * chance to run. A 5.1 user would see a syntax error from a file they did not + * write, about a version they were never told they needed. + * + * Running under 5.1 in CI proves it works on the day someone maintains that + * job. This proves it on the day someone writes `??`. + */ + +import { readFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { describe, expect, it } from 'vitest'; + +const REPO_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..'); +const script = (): string => readFileSync(join(REPO_ROOT, 'install.ps1'), 'utf8'); + +/** Code only, so a comment or a message may name any of these freely. */ +const statements = (text: string): string => + text + .split('\n') + .filter((line) => !line.trimStart().startsWith('#')) + .join('\n'); + +/** + * Constructs Windows PowerShell 5.1 cannot parse. Each is a parse-time failure, + * not a runtime one — which is what makes the floor worth asserting rather than + * discovering. + */ +const PS7_ONLY: ReadonlyArray<{ name: string; pattern: RegExp }> = [ + { name: 'null-coalescing (??)', pattern: /\?\?/ }, + { name: 'null-conditional (?.)', pattern: /\$\w+\?\./ }, + { name: 'pipeline chain (&& or ||)', pattern: /(^|\s)(&&|\|\|)(\s|$)/m }, + { name: 'ternary (? :)', pattern: /\)\s*\?\s*[^:\n]+\s*:\s*/ }, + { name: 'ForEach-Object -Parallel', pattern: /-Parallel\b/ }, + { name: 'Test-Json / ConvertFrom-Json -AsHashtable', pattern: /\bTest-Json\b|-AsHashtable\b/ }, +]; + +describe('#703 install.ps1 states the PowerShell floor it depends on', () => { + it.each(PS7_ONLY)('uses no $name', ({ pattern }) => { + expect(pattern.test(statements(script()))).toBe(false); + }); + + // The floor has to be readable by someone editing the file, not only by this + // test. A contributor who does not know 5.1 is supported cannot keep it. + it('says the floor in the file itself', () => { + const text = script(); + + expect(text, 'the supported version is named').toMatch(/PowerShell\s+5\.1/i); + expect( + text, + 'and why it is a floor rather than a preference', + ).toMatch(/parse|syntax|before|diagnos/i); + }); +});