Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion scripts/canary.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ function codePathsForFailure(name, phase) {
];
}

if (name.includes("open-target") || name.includes("window")) {
if (name.includes("window")) {
return ["scripts/lib/upstream-patches.mjs", "test/upstream-patches.test.mjs"];
}

Expand Down
208 changes: 2 additions & 206 deletions scripts/lib/upstream-patches.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,6 @@ import { parse } from "acorn";

import { linuxChromeExtensionHostContentVariantContract } from "./chrome-extension-patches.mjs";

const linuxOpenTargetDefinitions = ({ openCommandName, executableResolverName }) => [
"var __codexLinuxOpenTargetGotoArgs=(e,t)=>t?[`--goto`,`${e}:${t.line}:${t.column}`]:[e]",
"__codexLinuxOpenTargetColonArgs=(e,t)=>t?[`${e}:${t.line}:${t.column}`]:[e]",
`__codexLinuxOpenTargetTerminal=()=>{let e=process.env.TERMINAL?.trim();if(e&&${executableResolverName}(e))return{command:${executableResolverName}(e),args:e=>[\`-e\`,process.env.SHELL?.trim()||\`/bin/sh\`,\`-lc\`,e]};for(let e of [[\`ghostty\`,e=>[\`-e\`,process.env.SHELL?.trim()||\`/bin/sh\`,\`-lc\`,e]],[\`kitty\`,e=>[\`-e\`,process.env.SHELL?.trim()||\`/bin/sh\`,\`-lc\`,e]],[\`alacritty\`,e=>[\`-e\`,process.env.SHELL?.trim()||\`/bin/sh\`,\`-lc\`,e]],[\`wezterm\`,e=>[\`start\`,\`--\`,process.env.SHELL?.trim()||\`/bin/sh\`,\`-lc\`,e]],[\`gnome-terminal\`,e=>[\`--\`,process.env.SHELL?.trim()||\`/bin/sh\`,\`-lc\`,e]],[\`konsole\`,e=>[\`-e\`,process.env.SHELL?.trim()||\`/bin/sh\`,\`-lc\`,e]],[\`xterm\`,e=>[\`-e\`,process.env.SHELL?.trim()||\`/bin/sh\`,\`-lc\`,e]]]){let t=${executableResolverName}(e[0]);if(t)return{command:t,args:e[1]}}return null}`,
"__codexLinuxOpenTargetNvimArgs=(e,t)=>t?[`+call cursor(${t.line},${t.column})`,e]:[e]",
"__codexLinuxShellQuote=e=>{e=String(e);return e.length===0?`''`:/^[A-Za-z0-9_/:=.-]+$/.test(e)?e:`'${e.replaceAll(`'`,`'\\\\''`)}'`}",
"__codexLinuxOpenTargetNvimCommand=(e,n,r)=>[e,...__codexLinuxOpenTargetNvimArgs(n,r)].map(__codexLinuxShellQuote).join(` `)",
`__codexLinuxOpenTargetRunNvim=async({command:e,path:t,location:n})=>{let r=__codexLinuxOpenTargetTerminal();if(!r)throw Error(\`No terminal emulator found for Neovim\`);await ${openCommandName}(r.command,r.args(__codexLinuxOpenTargetNvimCommand(e,t,n)))}`,
`__codexLinuxVSCode={id:\`vscode\`,platforms:{linux:{label:\`VS Code\`,icon:\`apps/vscode.png\`,kind:\`editor\`,detect:()=>${executableResolverName}(\`code\`),args:__codexLinuxOpenTargetGotoArgs}}}`,
`__codexLinuxVSCodeInsiders={id:\`vscodeInsiders\`,platforms:{linux:{label:\`VS Code Insiders\`,icon:\`apps/vscode-insiders.png\`,kind:\`editor\`,detect:()=>${executableResolverName}(\`code-insiders\`),args:__codexLinuxOpenTargetGotoArgs}}}`,
`__codexLinuxCursor={id:\`cursor\`,platforms:{linux:{label:\`Cursor\`,icon:\`apps/cursor.png\`,kind:\`editor\`,detect:()=>${executableResolverName}(\`cursor\`),args:__codexLinuxOpenTargetGotoArgs}}}`,
`__codexLinuxZed={id:\`zed\`,platforms:{linux:{label:\`Zed\`,icon:\`apps/zed.png\`,kind:\`editor\`,detect:()=>${executableResolverName}(\`zed\`),args:__codexLinuxOpenTargetColonArgs}}}`,
`__codexLinuxNvim={id:\`nvim\`,platforms:{linux:{label:\`Neovim\`,icon:\`apps/terminal.png\`,kind:\`editor\`,detect:()=>${executableResolverName}(\`nvim\`),args:__codexLinuxOpenTargetNvimArgs,open:__codexLinuxOpenTargetRunNvim}}}`
].join(",");
const openTargetMapRegex =
/targets:\[\.\.\.([A-Za-z_$][\w$]*)\.map\(\(\{id:([A-Za-z_$][\w$]*),label:([A-Za-z_$][\w$]*),icon:([A-Za-z_$][\w$]*),kind:([A-Za-z_$][\w$]*),hidden:([A-Za-z_$][\w$]*)\}\)=>\(\{id:\2,target:\2,label:\3,icon:\4,kind:\5,hidden:\6,available:([A-Za-z_$][\w$]*)\.has\(\2\),default:([A-Za-z_$][\w$]*)===\2\|\|void 0\}\)\),\.\.\.([A-Za-z_$][\w$]*)\]/;
const linuxTransparencyPatchedRegex =
/transparent:[A-Za-z_$][\w$]*===`linux`\?!1:[A-Za-z_$][\w$]*,hasShadow:/;
const linuxTransparencyPatchRegex =
Expand Down Expand Up @@ -50,17 +34,6 @@ export const linuxChromeExtensionDetectionContract = {
assertAfter: assertLinuxChromeExtensionDetectionAfter
};
export const upstreamPatchContracts = [
// Why: upstream desktop only registers macOS open-in-editor targets; Linux
// needs locally installed editors and terminal-backed Neovim. Contract:
// upstream still exposes an open-target registry, runner, and preferred-target
// mapper. Repro: node scripts/canary.mjs --channel prod --no-smoke.
{
name: "open-target-dispatcher",
find: findOpenTargetRegistry,
assertBefore: assertOpenTargetsBefore,
apply: applyLinuxOpenTargetsSource,
assertAfter: assertOpenTargetsAfter
},
// Why: transparent frameless windows render poorly under Linux compositors.
// Contract: the main bundle still builds BrowserWindow background options
// from the parsed window-options object. Repro: node scripts/canary.mjs --channel prod --no-smoke.
Expand Down Expand Up @@ -139,20 +112,16 @@ export function patchUpstreamMainSource(source) {
return applyUpstreamPatchContracts(source, upstreamPatchContracts);
}

export function patchLinuxOpenTargetsSource(source) {
return applyUpstreamPatchContract(source, upstreamPatchContracts[0]);
}

export function patchLinuxChromeExtensionDetectionSource(source) {
return applyUpstreamPatchContract(source, linuxChromeExtensionDetectionContract);
}

export function patchDisableTransparencySource(source) {
return applyUpstreamPatchContracts(source, upstreamPatchContracts.slice(1, 3));
return applyUpstreamPatchContracts(source, upstreamPatchContracts.slice(0, 2));
}

export function patchLinuxWindowFocusableSource(source) {
return applyUpstreamPatchContract(source, upstreamPatchContracts[3]);
return applyUpstreamPatchContract(source, upstreamPatchContracts[2]);
}

export function patchLinuxOwlFeatureBindingSource(source) {
Expand Down Expand Up @@ -246,25 +215,6 @@ export function applyUpstreamPatchContract(source, contract) {
}
}

function applyLinuxOpenTargetsSource(source) {
let patched = source;

if (!patched.includes("__codexLinuxVSCode=")) {
const openTargets = findOpenTargetRegistry(patched);
patched = replaceOnce(
patched,
openTargets.anchor,
`${linuxOpenTargetDefinitions(openTargets)};${openTargets.anchor.replace("[", "[__codexLinuxVSCode,__codexLinuxVSCodeInsiders,__codexLinuxCursor,__codexLinuxZed,__codexLinuxNvim,")}`
);
}

patched = patchOpenTargetMap(patched);

patched = patchOpenTargetPlatformLookup(patched);

return patched;
}

async function patchOwlFeatureBindingChunks(buildDir, entries) {
for (const entry of entries) {
if (!entry.endsWith(".js")) {
Expand Down Expand Up @@ -376,29 +326,6 @@ function patchOwlFeatureBinding(source, patch = findOwlFeatureBindingPatch(sourc
return replaceOnce(source, patch.anchor, replacement);
}

function assertOpenTargetsBefore(source) {
findOpenTargetRegistry(source);
findOpenCommandName(source);

if (!source.includes("appPath:process.platform===`linux`") && !openTargetMapRegex.test(source)) {
throw new Error("missing open target map");
}
}

function assertOpenTargetsAfter(source) {
if (!source.includes("__codexLinuxVSCode=")) {
throw new Error("missing Linux open target definitions");
}

if (!source.includes("appPath:process.platform===`linux`")) {
throw new Error("missing Linux appPath target metadata");
}

if (source.includes("let n=t.platforms[e];return n")) {
throw new Error("open target platform lookup is not null-safe");
}
}

function patchLinuxWindowBackground(source) {
const patch = findLinuxWindowBackgroundPatch(source);

Expand Down Expand Up @@ -1162,137 +1089,6 @@ function assertLinuxWindowTransparencyAfter(source) {
}
}

function patchOpenTargetMap(source) {
if (source.includes("appPath:process.platform===`linux`")) {
return source;
}

const match = source.match(openTargetMapRegex);

if (!match) {
throw new Error("Unable to apply upstream patch; missing open target map");
}

const [
anchor,
targetsVar,
idVar,
labelVar,
iconVar,
kindVar,
hiddenVar,
availableSetVar,
defaultTargetVar,
extraTargetsVar
] = match;

const patchedMap =
`targets:[...${targetsVar}.map(({id:${idVar},label:${labelVar},icon:${iconVar},kind:${kindVar},hidden:${hiddenVar}})=>({` +
`id:${idVar},target:${idVar},label:${labelVar},icon:${iconVar},kind:${kindVar},hidden:${hiddenVar},` +
`appPath:process.platform===\`linux\`&&${kindVar}===\`editor\`&&${availableSetVar}.has(${idVar})?Ld().get(${idVar})??null:null,` +
`available:${availableSetVar}.has(${idVar}),default:${defaultTargetVar}===${idVar}||void 0})),...${extraTargetsVar}]`;

return replaceOnce(source, anchor, patchedMap);
}

function patchOpenTargetPlatformLookup(source) {
return source.replaceAll(
"let n=t.platforms[e];return n",
"let n=t.platforms?.[e];return n"
);
}

function findOpenTargetRegistry(source) {
const match = source.match(
/var ([A-Za-z_$][\w$]*)=\[[^\]]+\](?:,[A-Za-z_$][\w$]*=[A-Za-z_$][\w$]*\.[A-Za-z_$][\w$]*\(`open-in-targets`\)|\s*;[A-Za-z_$][\w$]*\.[A-Za-z_$][\w$]*\(`open-in-targets`\));\s*function [A-Za-z_$][\w$]*\(e\)\{return \1\.flatMap/
);

if (!match) {
throw new Error("Unable to apply upstream patch; missing open target registry");
}

const anchor = source.slice(match.index, source.indexOf("]", match.index) + 1);

return {
anchor,
openCommandName: findOpenCommandName(source),
executableResolverName: findOpenExecutableResolverName(source)
};
}

function findOpenExecutableResolverName(source) {
const resolverMatch = source.match(
/function ([A-Za-z_$][\w$]*)\(e\)\{let [A-Za-z_$][\w$]*=[A-Za-z_$][\w$]*\.default\.sync\(e,\{nothrow:!0\}\);return typeof [A-Za-z_$][\w$]*==`string`&&/
);

if (resolverMatch) {
return resolverMatch[1];
}

const targetDetectMatch = source.match(
/([A-Za-z_$][\w$]*)\(`(?:code|code-insiders|cursor|zed|nvim)`\)/
);

if (targetDetectMatch) {
return targetDetectMatch[1];
}

throw new Error("Unable to apply upstream patch; missing open target executable resolver");
}

function findOpenCommandName(source) {
const openDispatcherName = findOpenCommandNameFromDispatcher(source);

if (openDispatcherName) {
return openDispatcherName;
}

const match = source.match(
/await ([A-Za-z_$][\w$]*)\([A-Za-z_$][\w$]*,[A-Za-z_$][\w$]*\.args\([^)]*\),\{env:[A-Za-z_$][\w$]*\.env\?\.\(\)\}\)/
);

if (!match) {
throw new Error("Unable to apply upstream patch; missing open command runner");
}

return match[1];
}

function findOpenCommandNameFromDispatcher(source) {
const ast = parseJavaScript(source);
const names = new Set();

walkAst(ast, node => {
if (!isFunctionNode(node)) {
return;
}

const body = source.slice(node.start, node.end);

if (
!body.includes("Unknown open target") ||
!body.includes("Open target") ||
!body.includes(".args(")
) {
return;
}

const match = body.match(
/await ([A-Za-z_$][\w$]*)\([A-Za-z_$][\w$]*,[A-Za-z_$][\w$]*\.args\([^)]*\)(?:,\{env:[A-Za-z_$][\w$]*\.env\?\.\(\)\})?\)/
);

if (match) {
names.add(match[1]);
}
});

if (names.size > 1) {
throw new Error("Unable to apply upstream patch; ambiguous open command runner");
}

return names.values().next().value ?? null;
}

function replaceOnce(source, search, replacement) {
const index = source.indexOf(search);

Expand Down
12 changes: 6 additions & 6 deletions test/canary-issue.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import {
const failure = {
channel: "prod",
phase: "build",
failingName: "open-target-dispatcher",
failingName: "linux-window-background",
upstreamVersion: "26.616.30709",
upstreamBuildNumber: "30709",
packageVersion: "26.616.30709-launcher.29",
fingerprint: "prod:open-target-dispatcher:26.616.30709:30709",
errorMessage: "open-target-dispatcher contract changed: missing runner",
fingerprint: "prod:linux-window-background:26.616.30709:30709",
errorMessage: "linux-window-background contract changed: missing window background helper",
localReproductionCommand: "node scripts/canary.mjs --channel prod --json-output dist/upstream-canary-prod.json",
codePaths: ["scripts/lib/upstream-patches.mjs"],
publishBlockedBeforeMutation: true
Expand All @@ -25,7 +25,7 @@ const failure = {
test("canary issue title uses stable dedupe fields", () => {
assert.equal(
issueTitleForFailure(failure),
"Upstream canary failed: prod open-target-dispatcher 26.616.30709"
"Upstream canary failed: prod linux-window-background 26.616.30709"
);
});

Expand All @@ -42,9 +42,9 @@ test("canary issue body includes actionable repair evidence", () => {
assert.match(body, /Channel \| prod/);
assert.match(body, /Upstream build \| 30709/);
assert.match(body, /Package version \| 26\.616\.30709-launcher\.29/);
assert.match(body, /Contract\/smoke \| open-target-dispatcher/);
assert.match(body, /Contract\/smoke \| linux-window-background/);
assert.match(body, /Publish blocked before mutation \| yes/);
assert.match(body, /missing runner/);
assert.match(body, /missing window background helper/);
assert.match(body, /stack line/);
assert.match(body, /node scripts\/canary\.mjs --channel prod/);
assert.match(body, /scripts\/lib\/upstream-patches\.mjs/);
Expand Down
10 changes: 5 additions & 5 deletions test/canary-reporter.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ test("report-canary-failure creates exactly one actionable GitHub issue", async
{
channel: "prod",
phase: "build",
failingName: "open-target-dispatcher",
failingName: "linux-window-background",
upstreamVersion: "26.616.30709",
upstreamBuildNumber: "4108",
packageVersion: "26.616.30709-launcher.29",
fingerprint: "prod:open-target-dispatcher:26.616.30709:4108",
errorMessage: "open-target-dispatcher contract changed: missing runner",
fingerprint: "prod:linux-window-background:26.616.30709:4108",
errorMessage: "linux-window-background contract changed: missing window background helper",
localReproductionCommand: "node scripts/canary.mjs --channel prod",
codePaths: ["scripts/lib/upstream-patches.mjs"],
publishBlockedBeforeMutation: true
Expand Down Expand Up @@ -64,7 +64,7 @@ test("report-canary-failure creates exactly one actionable GitHub issue", async

assert.equal(issueMutations.length, 1);
assert.equal(issueMutations[0][1], "create");
assert.match(issueMutations[0].join(" "), /Upstream canary failed: prod open-target-dispatcher 26\.616\.30709/);
assert.match(issueMutations[0].join(" "), /Upstream canary failed: prod linux-window-background 26\.616\.30709/);

const body = await readBodyFileFromArgs(issueMutations[0]);
assert.match(body, /Workflow run \| https:\/\/github\.com\/better-slop\/codex-app-linux\/actions\/runs\/1/);
Expand All @@ -73,7 +73,7 @@ test("report-canary-failure creates exactly one actionable GitHub issue", async
assert.match(body, /Channel \| prod/);
assert.match(body, /Upstream build \| 4108/);
assert.match(body, /Package version \| 26\.616\.30709-launcher\.29/);
assert.match(body, /open-target-dispatcher contract changed/);
assert.match(body, /linux-window-background contract changed/);
assert.match(body, /canary log excerpt/);
assert.match(body, /scripts\/lib\/upstream-patches\.mjs/);
assert.match(body, /Publish blocked before mutation \| yes/);
Expand Down

This file was deleted.

Loading
Loading