mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
fix: preserve zsh prompt in packaged desktop (#544)
This commit is contained in:
@@ -9,6 +9,7 @@ files:
|
||||
- dist/**/*
|
||||
asarUnpack:
|
||||
- dist/daemon/node-entrypoint-runner.js
|
||||
- node_modules/@getpaseo/server/dist/server/terminal/shell-integration/**/*
|
||||
extraResources:
|
||||
- from: ../app/dist
|
||||
to: app-dist
|
||||
|
||||
19
packages/desktop/src/daemon/desktop-packaging.test.ts
Normal file
19
packages/desktop/src/daemon/desktop-packaging.test.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const packageRoot = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
||||
|
||||
describe("desktop packaging", () => {
|
||||
it("unpacks server zsh shell integration files for external shells", () => {
|
||||
const config = readFileSync(join(packageRoot, "electron-builder.yml"), "utf8");
|
||||
|
||||
expect(config).toContain(
|
||||
"node_modules/@getpaseo/server/dist/server/terminal/shell-integration/**/*",
|
||||
);
|
||||
expect(config).not.toContain(
|
||||
"node_modules/@getpaseo/server/dist/src/terminal/shell-integration/**/*",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from "./terminal.js";
|
||||
import {
|
||||
chmodSync,
|
||||
cpSync,
|
||||
existsSync,
|
||||
mkdtempSync,
|
||||
mkdirSync,
|
||||
@@ -18,6 +19,7 @@ import {
|
||||
statSync,
|
||||
writeFileSync,
|
||||
} from "node:fs";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { join } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
|
||||
@@ -208,7 +210,9 @@ describe("createTerminal", () => {
|
||||
|
||||
expect(resolvedEnv.TERM).toBe("xterm-256color");
|
||||
expect(resolvedEnv.PASEO_ZSH_ZDOTDIR).toBe("/tmp/paseo-zdotdir");
|
||||
expect(resolvedEnv.ZDOTDIR).toBe(resolveZshShellIntegrationDir());
|
||||
expect(resolvedEnv.ZDOTDIR).not.toBe("/tmp/paseo-zdotdir");
|
||||
expect(existsSync(join(resolvedEnv.ZDOTDIR, ".zshenv"))).toBe(true);
|
||||
expect(existsSync(join(resolvedEnv.ZDOTDIR, "paseo-integration.zsh"))).toBe(true);
|
||||
});
|
||||
|
||||
it("uses custom name when provided", async () => {
|
||||
@@ -588,6 +592,58 @@ describe("terminal title", () => {
|
||||
await waitForTitle(session, (title) => title === "~/dev/faro", 4000);
|
||||
});
|
||||
|
||||
it.skipIf(!hasZsh)("loads the user's zsh prompt when the integration dir is packaged", () => {
|
||||
const homeDir = mkdtempSync(join(tmpdir(), "terminal-zsh-packaged-home-"));
|
||||
temporaryDirs.push(homeDir);
|
||||
writeFileSync(join(homeDir, ".zshrc"), "PS1='PASEO_CUSTOM_PROMPT> '\n");
|
||||
|
||||
const fakeAppRoot = join(homeDir, "Paseo.app", "Contents", "Resources");
|
||||
const inaccessiblePackagedIntegrationDir = join(
|
||||
fakeAppRoot,
|
||||
"app.asar",
|
||||
"node_modules",
|
||||
"@getpaseo",
|
||||
"server",
|
||||
"dist",
|
||||
"server",
|
||||
"terminal",
|
||||
"shell-integration",
|
||||
"zsh",
|
||||
);
|
||||
const unpackedIntegrationDir = join(
|
||||
fakeAppRoot,
|
||||
"app.asar.unpacked",
|
||||
"node_modules",
|
||||
"@getpaseo",
|
||||
"server",
|
||||
"dist",
|
||||
"server",
|
||||
"terminal",
|
||||
"shell-integration",
|
||||
"zsh",
|
||||
);
|
||||
mkdirSync(unpackedIntegrationDir, { recursive: true });
|
||||
cpSync(resolveZshShellIntegrationDir(), unpackedIntegrationDir, { recursive: true });
|
||||
writeFileSync(join(fakeAppRoot, "app.asar"), "asar archive placeholder");
|
||||
|
||||
const env = buildTerminalEnvironment({
|
||||
shell: "/bin/zsh",
|
||||
env: {
|
||||
HOME: homeDir,
|
||||
},
|
||||
zshShellIntegrationDir: inaccessiblePackagedIntegrationDir,
|
||||
});
|
||||
|
||||
const result = spawnSync("/bin/zsh", ["-i", "-c", "print -r -- ${PROMPT}"], {
|
||||
cwd: homeDir,
|
||||
env,
|
||||
encoding: "utf8",
|
||||
});
|
||||
|
||||
expect(result.status).toBe(0);
|
||||
expect(result.stdout.split(/\r?\n/)).toContain("PASEO_CUSTOM_PROMPT> ");
|
||||
});
|
||||
|
||||
it.skipIf(!hasZsh)("emits zsh shell integration command completion", async () => {
|
||||
const homeDir = mkdtempSync(join(tmpdir(), "terminal-zsh-command-finished-home-"));
|
||||
temporaryDirs.push(homeDir);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import * as pty from "node-pty";
|
||||
import xterm, { type Terminal as TerminalType } from "@xterm/headless";
|
||||
import { randomUUID } from "crypto";
|
||||
import { chmodSync, existsSync, statSync } from "node:fs";
|
||||
import { chmodSync, copyFileSync, existsSync, mkdirSync, statSync } from "node:fs";
|
||||
import { tmpdir, userInfo } from "node:os";
|
||||
import { basename, dirname, join } from "node:path";
|
||||
import { createRequire } from "node:module";
|
||||
import { fileURLToPath } from "node:url";
|
||||
@@ -85,6 +86,7 @@ export interface CreateTerminalOptions {
|
||||
interface BuildTerminalEnvironmentInput {
|
||||
shell: string;
|
||||
env: Record<string, string>;
|
||||
zshShellIntegrationDir?: string;
|
||||
}
|
||||
|
||||
export interface CaptureTerminalLinesOptions {
|
||||
@@ -182,6 +184,33 @@ export function resolveZshShellIntegrationDir(): string {
|
||||
return fileURLToPath(new URL("./shell-integration/zsh", import.meta.url));
|
||||
}
|
||||
|
||||
function resolveExternalProcessPath(filePath: string): string {
|
||||
return filePath.replace(/\.asar(?=\/|$)/, ".asar.unpacked");
|
||||
}
|
||||
|
||||
function resolveZshShellIntegrationRuntimeDir(): string {
|
||||
let username = "unknown";
|
||||
try {
|
||||
username = userInfo().username || username;
|
||||
} catch {
|
||||
// keep fallback
|
||||
}
|
||||
return join(tmpdir(), `${username}-paseo-zsh`);
|
||||
}
|
||||
|
||||
function prepareZshShellIntegrationRuntimeDir(sourceDir = resolveZshShellIntegrationDir()): string {
|
||||
const readableSourceDir = resolveExternalProcessPath(sourceDir);
|
||||
const runtimeDir = resolveZshShellIntegrationRuntimeDir();
|
||||
mkdirSync(runtimeDir, { recursive: true, mode: 0o700 });
|
||||
chmodSync(runtimeDir, 0o700);
|
||||
copyFileSync(join(readableSourceDir, ".zshenv"), join(runtimeDir, ".zshenv"));
|
||||
copyFileSync(
|
||||
join(readableSourceDir, "paseo-integration.zsh"),
|
||||
join(runtimeDir, "paseo-integration.zsh"),
|
||||
);
|
||||
return runtimeDir;
|
||||
}
|
||||
|
||||
export function buildTerminalEnvironment(
|
||||
input: BuildTerminalEnvironmentInput,
|
||||
): Record<string, string> {
|
||||
@@ -199,7 +228,7 @@ export function buildTerminalEnvironment(
|
||||
return {
|
||||
...baseEnv,
|
||||
PASEO_ZSH_ZDOTDIR: originalZdotdir,
|
||||
ZDOTDIR: resolveZshShellIntegrationDir(),
|
||||
ZDOTDIR: prepareZshShellIntegrationRuntimeDir(input.zshShellIntegrationDir),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user