Skip to content
Merged
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
25 changes: 5 additions & 20 deletions apps/desktop/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import * as Electron from "electron";

import * as NetService from "@t3tools/shared/Net";
import { HostProcessArchitecture, HostProcessPlatform } from "@t3tools/shared/hostProcess";
import { isArchiveDistributedVersion } from "@t3tools/shared/cliRelease";
import { resolveRemoteT3CliPackageSpec } from "@t3tools/ssh/command";
import type { RemoteT3RunnerOptions } from "@t3tools/ssh/tunnel";
import serverPackageJson from "../../server/package.json" with { type: "json" };

Expand Down Expand Up @@ -87,9 +85,11 @@ const desktopEnvironmentLayer = Layer.unwrap(
}),
);

// The remote runs the exact release this app is on, from its self-contained
// archive, so it needs neither Node nor npm. Development points the remote at
// a source checkout instead so the two sides can be iterated together.
const resolveDesktopSshCliRunner = (
environment: DesktopEnvironment.DesktopEnvironment["Service"],
settings: DesktopAppSettings.DesktopSettings,
): RemoteT3RunnerOptions => {
const devRemoteEntryPath = Option.getOrUndefined(environment.devRemoteT3ServerEntryPath);
if (environment.isDevelopment && devRemoteEntryPath !== undefined) {
Expand All @@ -98,29 +98,14 @@ const resolveDesktopSshCliRunner = (
nodeEngineRange: serverPackageJson.engines.node,
};
}
// Preview builds ship as self-contained archives, so the remote runs the
// same version this app is on without Node or npm.
if (!environment.isDevelopment && isArchiveDistributedVersion(environment.appVersion)) {
return { archiveVersion: environment.appVersion };
}
return {
packageSpec: resolveRemoteT3CliPackageSpec({
appVersion: environment.appVersion,
updateChannel: settings.updateChannel,
isDevelopment: environment.isDevelopment,
}),
nodeEngineRange: serverPackageJson.engines.node,
};
return { archiveVersion: environment.appVersion };
};

const desktopSshEnvironmentLayer = Layer.unwrap(
Effect.gen(function* () {
const environment = yield* DesktopEnvironment.DesktopEnvironment;
const settings = yield* DesktopAppSettings.DesktopAppSettings;
return DesktopSshEnvironment.layer({
resolveCliRunner: settings.get.pipe(
Effect.map((currentSettings) => resolveDesktopSshCliRunner(environment, currentSettings)),
),
resolveCliRunner: Effect.succeed(resolveDesktopSshCliRunner(environment)),
});
}),
);
Expand Down
12 changes: 4 additions & 8 deletions apps/desktop/src/ssh/DesktopSshEnvironment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ export class DesktopSshEnvironment extends Context.Service<
>()("@t3tools/desktop/ssh/DesktopSshEnvironment") {}

export interface DesktopSshEnvironmentLayerOptions {
readonly resolveCliPackageSpec?: () => string;
readonly resolveCliRunner?: Effect.Effect<SshTunnel.RemoteT3RunnerOptions>;
}

Expand Down Expand Up @@ -168,13 +167,10 @@ export const make = Effect.gen(function* () {
export const layer = (options: DesktopSshEnvironmentLayerOptions = {}) =>
Layer.effect(DesktopSshEnvironment, make).pipe(
Layer.provide(
SshTunnel.SshEnvironmentManager.layer({
...(options.resolveCliPackageSpec === undefined
SshTunnel.SshEnvironmentManager.layer(
options.resolveCliRunner === undefined
? {}
: { resolveCliPackageSpec: options.resolveCliPackageSpec }),
...(options.resolveCliRunner === undefined
? {}
: { resolveCliRunner: options.resolveCliRunner }),
}),
: { resolveCliRunner: options.resolveCliRunner },
),
),
);
2 changes: 2 additions & 0 deletions apps/marketing/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/public/install.sh
/public/install.ps1
5 changes: 3 additions & 2 deletions apps/marketing/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
"private": true,
"type": "module",
"scripts": {
"dev": "astro dev",
"build": "astro build",
"stage:install-scripts": "node scripts/stage-install-scripts.mjs",
"dev": "node scripts/stage-install-scripts.mjs && astro dev",
"build": "node scripts/stage-install-scripts.mjs && astro build",
"preview": "astro preview",
"typecheck": "astro check"
},
Expand Down
15 changes: 15 additions & 0 deletions apps/marketing/scripts/stage-install-scripts.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// The CLI install scripts live in scripts/ at the repo root with the rest of
// the release tooling; the site serves them at /install.sh and /install.ps1.
// Copy them into public/ before every Astro build and dev server so the two
// never drift. The copies are gitignored.
import * as NodeFS from "node:fs";
import * as NodePath from "node:path";
import * as NodeURL from "node:url";

const marketingDir = NodePath.dirname(NodePath.dirname(NodeURL.fileURLToPath(import.meta.url)));
const repoRoot = NodePath.dirname(NodePath.dirname(marketingDir));
const publicDir = NodePath.join(marketingDir, "public");
NodeFS.mkdirSync(publicDir, { recursive: true });
for (const name of ["install.sh", "install.ps1"]) {
NodeFS.copyFileSync(NodePath.join(repoRoot, "scripts", name), NodePath.join(publicDir, name));
}
2 changes: 1 addition & 1 deletion apps/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"type": "module",
"scripts": {
"dev": "node --watch src/bin.ts",
"build:bundle": "vp pack && vp pack src/service-launcher.ts --out-dir dist --no-clean",
"build:bundle": "vp pack",
"build:exe": "node scripts/cli.ts build-exe",
"start": "node dist/bin.mjs",
"typecheck": "tsc --noEmit",
Expand Down
6 changes: 1 addition & 5 deletions apps/server/scripts/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,11 +284,7 @@ const publishCmd = Command.make(
const packageJsonPath = path.join(serverDir, "package.json");

// Assert build assets exist
for (const relPath of [
"dist/bin.mjs",
"dist/service-launcher.mjs",
"dist/client/index.html",
]) {
for (const relPath of ["dist/bin.mjs", "dist/client/index.html"]) {
const abs = path.join(serverDir, relPath);
if (!(yield* fs.exists(abs))) {
return yield* new ServerCliBuildAssetMissingError({ assetPath: abs });
Expand Down
3 changes: 1 addition & 2 deletions apps/server/src/cli/service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,7 @@ function makeTestService(serviceStatus: BootService.BootServiceStatus) {
Effect.sync(() => {
installOptions.push(options);
return {
program: ["/test/node", "/test/service-launcher.mjs"],
launcherPath: "/test/service-launcher.mjs",
program: ["/test/t3/runtime/versions/1.0.0/t3", "__service-launcher"],
baseDir: "/test/t3",
unitPath: serviceStatus.unitPath,
logPath: serviceStatus.logPath,
Expand Down
7 changes: 3 additions & 4 deletions apps/server/src/cli/serviceLauncher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ import { Command } from "effect/unstable/cli";
import { main as runServiceLauncher } from "../serviceLauncher.ts";

/**
* Hosts the service launcher inside the CLI executable. Archive-distributed
* runtimes have no Node on the machine to run `service-launcher.mjs`, so the
* service manager runs `t3 __service-launcher` and the launcher spawns the
* server from the same executable.
* Hosts the service launcher inside the CLI executable. The service manager
* runs `t3 __service-launcher` and the launcher spawns the server from the
* same executable, so the machine needs no Node to run either.
*
* The launcher owns SIGTERM handling and the process lifetime: it must finish
* stopping its child before the process exits, so it runs detached from the
Expand Down
17 changes: 3 additions & 14 deletions apps/server/src/cli/update.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {
HostProcessArchitecture,
HostProcessEnvironment,
HostProcessExecutablePath,
HostProcessInvokedAs,
HostProcessIsExecutable,
HostProcessPlatform,
Expand All @@ -12,7 +11,6 @@ import {
CLI_RELEASE_CHANNELS,
cliReleaseIndexPageUrl,
cliReleaseChannelOf,
isArchiveDistributedVersion,
newestCliReleaseVersion,
type CliReleaseChannel,
} from "@t3tools/shared/cliRelease";
Expand Down Expand Up @@ -320,7 +318,7 @@ const belongsToBootService = Effect.fn("cli.update.belongs_to_boot_service")(fun
Effect.map((result) => (result.code === 0 ? result.stdout : "")),
Effect.orElseSucceed(() => ""),
);
return /__service-launcher|service-launcher\.mjs/.test(command);
return /__service-launcher/.test(command);
}
return false;
});
Expand All @@ -338,7 +336,6 @@ const runUpdate = Effect.fn("cli.update.run")(function* (input: {
const runner = yield* ProcessRunner.ProcessRunner;
const platform = yield* HostProcessPlatform;
const arch = yield* HostProcessArchitecture;
const execPath = yield* HostProcessExecutablePath;
const environment = yield* HostProcessEnvironment;
const httpClient = yield* HttpClient.HttpClient;
const service = yield* BootService.BootService;
Expand Down Expand Up @@ -410,14 +407,6 @@ const runUpdate = Effect.fn("cli.update.run")(function* (input: {
? serviceVersion
: currentVersion;

// Only archive-distributed versions install without Node and npm on the
// machine. Until nightly and stable ship archives, updating onto them from
// here would reintroduce the dependency this command exists to remove.
if (!isArchiveDistributedVersion(targetVersion)) {
return yield* new CliUpdateError({
reason: `t3@${targetVersion} is published on npm only. Install it with \`npm install -g t3@${targetVersion}\`, or pick a version from the preview channel.`,
});
}
if (executableCurrent && serviceCurrent) {
yield* Console.log(
serviceVersion !== undefined
Expand Down Expand Up @@ -480,8 +469,8 @@ const runUpdate = Effect.fn("cli.update.run")(function* (input: {
validate: (paths) =>
runner
.run({
command: pinnedRuntimeCommand(paths, execPath).command,
args: [...pinnedRuntimeCommand(paths, execPath).args, "--version"],
command: pinnedRuntimeCommand(paths).command,
args: [...pinnedRuntimeCommand(paths).args, "--version"],
timeout: Duration.seconds(30),
})
.pipe(
Expand Down
Loading
Loading