Skip to content
Open
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
8 changes: 8 additions & 0 deletions apps/server/src/cli/invocation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,15 @@ it.layer(NodeServices.layer)("root CLI commands", (it) => {

expect(yield* rootCommand(desktop)).toBe(`sudo '${shim}' browser setup`);

// An unrelated `t3` on PATH, such as an older npm global, is another install's CLI.
expect(yield* rootCommand({ ...desktop, path: yield* pathWithT3 })).toBe(
`sudo '${shim}' browser setup`,
);

// The link the app installs onto PATH leads to its own launcher, so plain `t3` is right.
const linked = yield* fs.makeTempDirectoryScoped();
yield* fs.symlink(shim, path.join(linked, "t3"));
expect(yield* rootCommand({ ...desktop, path: linked })).toBe(
'sudo env "PATH=$PATH" t3 browser setup',
);

Expand Down
15 changes: 12 additions & 3 deletions apps/server/src/cli/invocation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as Schema from "effect/Schema";

import type { ServerInstallation } from "@t3tools/contracts";
import * as HostProcess from "@t3tools/shared/HostProcess";
import { isCommandAvailable } from "@t3tools/shared/shell";
import { resolveCommandPath } from "@t3tools/shared/shell";

import packageJson from "../../package.json" with { type: "json" };

Expand Down Expand Up @@ -143,7 +143,7 @@ const resolveInstallLauncher = Effect.gen(function* () {
});

/**
* Uses `t3` on PATH, the originating package runner, or this install's absolute launcher path.
* Uses this install's `t3` on PATH, the originating runner, or its absolute launcher path.
* Arguments are quoted for POSIX shells or PowerShell on Windows.
*/
export const resolveHostCliCommand = Effect.fn("invocation.resolveHostCliCommand")(function* (
Expand All @@ -155,7 +155,16 @@ export const resolveHostCliCommand = Effect.fn("invocation.resolveHostCliCommand
[prefix, ...args.map((arg) => hostShellWord(platform, arg))].join(" ");
const command = yield* resolveCliCommand(subcommand);
if (command !== `t3 ${subcommand}`) return { command: withArgs(command), launcher: false };
if (yield* isCommandAvailable("t3")) return { command: withArgs(command), launcher: false };
const onPath = yield* resolveCommandPath("t3");
if (onPath !== null) {
const shim = (yield* HostProcess.Environment).T3CODE_CLI_PATH?.trim();
const fs = yield* FileSystem.FileSystem;
const real = (file: string) => fs.realPath(file).pipe(Effect.orElseSucceed(() => null));
const shimReal = shim ? yield* real(shim) : null;
if (shimReal === null || shimReal === (yield* real(onPath))) {
return { command: withArgs(command), launcher: false };
}
}
const launcher = yield* resolveInstallLauncher;
if (Option.isNone(launcher)) return { command: withArgs(command), launcher: false };
// PowerShell runs a quoted path only through the call operator.
Expand Down
Loading