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
2 changes: 2 additions & 0 deletions .github/workflows/update-t3code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ jobs:
branch: automation/update-t3code-${{ steps.release.outputs.result }}
delete-branch: true
draft: true
# Bot-created PRs don't notify anyone unless a person is requested.
reviewers: TB516
commit-message: "chore(flatpak): update T3 Code to ${{ steps.release.outputs.result }}"
title: Update T3 Code to ${{ steps.release.outputs.result }}
body: |
Expand Down
11 changes: 10 additions & 1 deletion flatpak/com.t3tools.t3code.metainfo.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,18 @@
<launchable type="desktop-id">com.t3tools.t3code.desktop</launchable>
<url type="homepage">https://t3.codes</url>
<releases>
<release version="0.1.1-1" date="2026-10-21">
<url type="details">https://github.com/pingdotgg/t3code/releases/tag/v0.1.1</url>
<description>
<p>Update to T3 Code 0.1.1 with Electron 44.9.1, which no longer trusts
the compromised eMudhra intermediate certificates.</p>
<p>Reclaim database space from Settings without installing a command-line
launcher. Update track changes are disabled because Flatpak manages updates.</p>
</description>
</release>
<release version="0.1.0" date="2026-10-15">
<url type="details">https://github.com/pingdotgg/t3code/releases/tag/v0.1.0</url>
<description></description>
<description/>
</release>
<release version="0.0.45" date="2026-10-02">
<url type="details">https://github.com/pingdotgg/t3code/releases/tag/v0.0.45</url>
Expand Down
9 changes: 7 additions & 2 deletions flatpak/modules/t3code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,14 @@ test-commands:
src/environment/ServerEnvironment.test.ts
src/preview/PreviewBrowserHost.test.ts
src/preview/ServerBrowserStream.test.ts
src/cli/doctor.test.ts
src/persistence/DatabaseMaintenance.test.ts
- pnpm --filter t3 typecheck
sources:
- type: git
url: https://github.com/pingdotgg/t3code.git
tag: v0.1.0
commit: 81493efb71e8e8244f95d3e9f71d8369654ab151
tag: v0.1.1
commit: fe27e8e3dc12dcbb108e9a28fd9b8cb65f34ae92
x-checker-data:
type: json
url: https://api.github.com/repos/pingdotgg/t3code/releases/latest
Expand All @@ -77,6 +79,9 @@ sources:
- ../../patches/0008-flatpak-cli-launcher.patch
- ../../patches/0009-flatpak-server-updates.patch
- ../../patches/0010-flatpak-browser-requirements.patch
- ../../patches/0011-flatpak-database-maintenance.patch
- ../../patches/0012-flatpak-update-channel.patch
- ../../patches/0013-electron-security-update.patch
use-git: true
- type: dir
path: ../package
Expand Down
6 changes: 6 additions & 0 deletions flatpak/package/host-server.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,10 @@ unset T3CODE_DESKTOP_HTTPS_ENDPOINTS T3CODE_TAILSCALE_SERVE T3CODE_TAILSCALE_SER
cd "$HOME"

export ELECTRON_RUN_AS_NODE=1
# Desktop maintenance runs with the same runtime and sanitized host environment,
# after the desktop has stopped its server. No user-facing CLI is installed.
if [ "${1:-}" = "--compact-database" ]; then
exec "$T3CODE_FLATPAK_SERVER" --require "$T3CODE_FLATPAK_COMPILE_CACHE" "$T3CODE_FLATPAK_ENTRY" flatpak-compact-database --base-dir "$T3CODE_HOME"
fi

exec "$T3CODE_FLATPAK_SERVER" --require "$T3CODE_FLATPAK_COMPILE_CACHE" "$T3CODE_FLATPAK_ENTRY" --bootstrap-fd 3
77 changes: 37 additions & 40 deletions patches/0001-flatpak-desktop-integration.patch
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ index 773ea2450..8eb05c265 100644
devServerUrl: Config.URL("VITE_DEV_SERVER_URL").pipe(Config.option),
appUserModelIdOverride: trimmedString("T3CODE_DESKTOP_APP_USER_MODEL_ID"),
diff --git a/apps/desktop/src/app/DesktopEarlyElectronStartup.ts b/apps/desktop/src/app/DesktopEarlyElectronStartup.ts
index c4adf8f3c..06a5cb46d 100644
index 0015e6426..9c792f7ed 100644
--- a/apps/desktop/src/app/DesktopEarlyElectronStartup.ts
+++ b/apps/desktop/src/app/DesktopEarlyElectronStartup.ts
@@ -1,3 +1,5 @@
Expand All @@ -20,7 +20,7 @@ index c4adf8f3c..06a5cb46d 100644
import { fromLenientJson } from "@t3tools/shared/schemaJson";
import * as Option from "effect/Option";
import * as Schema from "effect/Schema";
@@ -158,10 +160,11 @@ export function resolveEarlyLinuxElectronOptions(
@@ -149,10 +151,11 @@ export function resolveEarlyLinuxElectronOptions(
): EarlyLinuxElectronOptions {
const preference = resolveEarlyLinuxPasswordStorePreference(input);
const isDevelopment = isDevelopmentEnvironment(input.env);
Expand Down Expand Up @@ -58,10 +58,10 @@ index fb6c5a8b1..b8be6acf4 100644
Effect.gen(function* () {
const environment = yield* makeEnvironment(
diff --git a/apps/desktop/src/app/DesktopEnvironment.ts b/apps/desktop/src/app/DesktopEnvironment.ts
index f697e0456..bb0a28ece 100644
index ecf08255f..04934beb7 100644
--- a/apps/desktop/src/app/DesktopEnvironment.ts
+++ b/apps/desktop/src/app/DesktopEnvironment.ts
@@ -175,10 +175,14 @@ const make = Effect.fn("desktop.environment.make")(function* (
@@ -167,10 +167,14 @@ const make = Effect.fn("desktop.environment.make")(function* (
input.isPackaged && input.platform === "win32"
? path.join(input.resourcesPath, "server.asar")
: appRoot;
Expand All @@ -77,7 +77,7 @@ index f697e0456..bb0a28ece 100644
const displayName = branding.displayName;
const stateDir = resolveDesktopStateDir({
baseDir,
@@ -238,8 +242,10 @@ const make = Effect.fn("desktop.environment.make")(function* (
@@ -230,8 +234,10 @@ const make = Effect.fn("desktop.environment.make")(function* (
appUserModelId: Option.getOrElse(config.appUserModelIdOverride, () =>
isDevelopment ? "com.t3tools.t3code.dev" : "com.t3tools.t3code",
),
Expand All @@ -91,16 +91,17 @@ index f697e0456..bb0a28ece 100644
appImagePath: config.appImagePath,
defaultDesktopSettings: DesktopAppSettings.resolveDefaultDesktopSettings(input.appVersion),
diff --git a/apps/desktop/src/app/DesktopLinuxUrlHandler.test.ts b/apps/desktop/src/app/DesktopLinuxUrlHandler.test.ts
index cbabe14dd..209853d37 100644
index 295f63eaf..2e2f315cb 100644
--- a/apps/desktop/src/app/DesktopLinuxUrlHandler.test.ts
+++ b/apps/desktop/src/app/DesktopLinuxUrlHandler.test.ts
@@ -1,4 +1,4 @@
@@ -1,5 +1,5 @@
import * as NodeServices from "@effect/platform-node/NodeServices";
-import { assert, describe, it } from "@effect/vitest";
+import { assert, describe, it, vi } from "@effect/vitest";
import * as Deferred from "effect/Deferred";
import * as Effect from "effect/Effect";
import * as Fiber from "effect/Fiber";
@@ -158,6 +158,15 @@ const emptyRecording = (): RecordedRegistration => ({
@@ -159,6 +159,15 @@ const emptyRecording = (): RecordedRegistration => ({
});

describe("DesktopLinuxUrlHandler", () => {
Expand All @@ -117,7 +118,7 @@ index cbabe14dd..209853d37 100644
const entry = DesktopLinuxUrlHandler.renderUrlHandlerDesktopEntry({
displayName: "T3 Code (Nightly)",
diff --git a/apps/desktop/src/app/DesktopLinuxUrlHandler.ts b/apps/desktop/src/app/DesktopLinuxUrlHandler.ts
index e67cdcefe..f61264212 100644
index e03418964..bf2c12f4e 100644
--- a/apps/desktop/src/app/DesktopLinuxUrlHandler.ts
+++ b/apps/desktop/src/app/DesktopLinuxUrlHandler.ts
@@ -9,6 +9,7 @@ import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner";
Expand All @@ -128,7 +129,7 @@ index e67cdcefe..f61264212 100644
import * as DesktopEnvironment from "./DesktopEnvironment.ts";
import { makeComponentLogger } from "./DesktopObservability.ts";

@@ -220,7 +221,10 @@ export const make = Effect.gen(function* () {
@@ -292,7 +293,10 @@ export const make = Effect.gen(function* () {
);

const register = Effect.gen(function* () {
Expand All @@ -141,10 +142,10 @@ index e67cdcefe..f61264212 100644
}
yield* writeDesktopEntry;
diff --git a/apps/desktop/src/app/DesktopPreReadyPlatform.test.ts b/apps/desktop/src/app/DesktopPreReadyPlatform.test.ts
index dd15f879f..08731decb 100644
index d27251d7b..cb5dfad04 100644
--- a/apps/desktop/src/app/DesktopPreReadyPlatform.test.ts
+++ b/apps/desktop/src/app/DesktopPreReadyPlatform.test.ts
@@ -58,6 +58,28 @@ vi.mock("node:child_process", () => ({
@@ -60,6 +60,28 @@ vi.mock("node:child_process", () => ({
import * as DesktopPreReadyPlatform from "./DesktopPreReadyPlatform.ts";

describe("DesktopPreReadyPlatform", () => {
Expand Down Expand Up @@ -174,7 +175,7 @@ index dd15f879f..08731decb 100644
appendSwitchMock.mockReset();
getSwitchValueMock.mockReset();
diff --git a/apps/desktop/src/app/DesktopPreReadyPlatform.ts b/apps/desktop/src/app/DesktopPreReadyPlatform.ts
index b021ae02e..b434b2a77 100644
index 58670439a..902fec467 100644
--- a/apps/desktop/src/app/DesktopPreReadyPlatform.ts
+++ b/apps/desktop/src/app/DesktopPreReadyPlatform.ts
@@ -9,6 +9,7 @@ import * as Layer from "effect/Layer";
Expand All @@ -185,8 +186,8 @@ index b021ae02e..b434b2a77 100644
import * as DesktopEarlyElectronStartup from "./DesktopEarlyElectronStartup.ts";
import { resolveDesktopAppBranding } from "./DesktopEnvironment.ts";
import { renderUrlHandlerDesktopEntry } from "./DesktopLinuxUrlHandler.ts";
@@ -64,50 +65,52 @@ export const make = Effect.gen(function* () {
if (linux !== null) {
@@ -75,47 +76,49 @@ export const make = Effect.gen(function* () {
});
// The portal also requires a valid desktop entry. An AppImage update may
// have removed the executable referenced by the previous launch's entry.
- try {
Expand Down Expand Up @@ -214,8 +215,22 @@ index b021ae02e..b434b2a77 100644
- ),
- iconPath,
- );
- } catch {
- // Icon installation is optional; registration retries after readiness.
- } catch {}
- }
- NodeFS.writeFileSync(
- NodePath.posix.join(applicationsDir, linux.linuxDesktopEntryName),
- renderUrlHandlerDesktopEntry({
- displayName: resolveDesktopAppBranding({
- isDevelopment: linux.isDevelopment,
- appVersion: Electron.app.getVersion(),
- }).displayName,
- execTarget: process.env.APPIMAGE?.trim() || process.execPath,
- scheme: ElectronProtocol.getDesktopScheme(linux.isDevelopment),
- ...(iconPath === undefined ? {} : { iconPath }),
- }),
- "utf8",
- );
- } catch {}
+ if (flatpakAppId(process.env, platform) === null) {
+ try {
+ const applicationsDir = NodePath.posix.join(
Expand All @@ -242,10 +257,8 @@ index b021ae02e..b434b2a77 100644
+ ),
+ iconPath,
+ );
+ } catch {
+ // Icon installation is optional; registration retries after readiness.
+ }
}
+ } catch {}
+ }
+ NodeFS.writeFileSync(
+ NodePath.posix.join(applicationsDir, linux.linuxDesktopEntryName),
+ renderUrlHandlerDesktopEntry({
Expand All @@ -259,27 +272,11 @@ index b021ae02e..b434b2a77 100644
+ }),
+ "utf8",
+ );
+ } catch {
+ // The URL handler retries with the full environment and logs failures.
}
- NodeFS.writeFileSync(
- NodePath.posix.join(applicationsDir, linux.linuxDesktopEntryName),
- renderUrlHandlerDesktopEntry({
- displayName: resolveDesktopAppBranding({
- isDevelopment: linux.isDevelopment,
- appVersion: Electron.app.getVersion(),
- }).displayName,
- execTarget: process.env.APPIMAGE?.trim() || process.execPath,
- scheme: ElectronProtocol.getDesktopScheme(linux.isDevelopment),
- ...(iconPath === undefined ? {} : { iconPath }),
- }),
- "utf8",
- );
- } catch {
- // The URL handler retries with the full environment and logs failures.
}
+ } catch {}
+ }
// Chromium caches its portal registration during startup. Set the identity
// before any asynchronous work can initialize it with Electron's default.
Electron.app.setDesktopName(linux.linuxDesktopEntryName);
diff --git a/apps/desktop/src/app/Flatpak.ts b/apps/desktop/src/app/Flatpak.ts
new file mode 100644
index 000000000..006f94e00
Expand Down
6 changes: 3 additions & 3 deletions patches/0002-flatpak-host-backend.patch
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
diff --git a/apps/desktop/src/backend/DesktopBackendConfiguration.ts b/apps/desktop/src/backend/DesktopBackendConfiguration.ts
index b306975be..7f70a3cfc 100644
index dadbe8acc..79e41c7b6 100644
--- a/apps/desktop/src/backend/DesktopBackendConfiguration.ts
+++ b/apps/desktop/src/backend/DesktopBackendConfiguration.ts
@@ -1,5 +1,8 @@
Expand All @@ -11,7 +11,7 @@ index b306975be..7f70a3cfc 100644
import { parsePersistedServerObservabilitySettings } from "@t3tools/shared/serverSettings";
import { currentDesktopBootstrapToken } from "@t3tools/shared/desktopBootstrapToken";
import * as Clock from "effect/Clock";
@@ -584,7 +587,7 @@ const resolvePrimaryStartConfig = Effect.fn("desktop.backendConfiguration.resolv
@@ -535,7 +538,7 @@ const resolvePrimaryStartConfig = Effect.fn("desktop.backendConfiguration.resolv
...buildObservabilityFragment(input.observabilitySettings),
};

Expand All @@ -20,7 +20,7 @@ index b306975be..7f70a3cfc 100644
executablePath: process.execPath,
// Packaged builds only, so a dev instance never shares the cache with the
// prod app it is often run from. `--require` rather than NODE_COMPILE_CACHE,
@@ -612,6 +615,15 @@ const resolvePrimaryStartConfig = Effect.fn("desktop.backendConfiguration.resolv
@@ -561,6 +564,15 @@ const resolvePrimaryStartConfig = Effect.fn("desktop.backendConfiguration.resolv
captureOutput: true,
preflightFailure: Option.none(),
} satisfies DesktopBackendManager.DesktopBackendStartConfig;
Expand Down
32 changes: 16 additions & 16 deletions patches/0003-flatpak-host-environment.patch
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
diff --git a/apps/desktop/src/shell/DesktopShellEnvironment.test.ts b/apps/desktop/src/shell/DesktopShellEnvironment.test.ts
index 6efe81757..c453e6274 100644
index 3adf74bf7..141319707 100644
--- a/apps/desktop/src/shell/DesktopShellEnvironment.test.ts
+++ b/apps/desktop/src/shell/DesktopShellEnvironment.test.ts
@@ -120,6 +120,21 @@ function runShellEnvironment(input: {
@@ -126,6 +126,21 @@ function runShellEnvironment(input: {
}

describe("DesktopShellEnvironment", () => {
Expand All @@ -21,31 +21,31 @@ index 6efe81757..c453e6274 100644
+ }).pipe(Effect.tap(() => Effect.sync(() => assert.equal(env.PATH, "/app/bin:/usr/bin"))));
+ });
+
it.effect("rejects PATH output from a failed shell and tries the next candidate", () =>
Effect.gen(function* () {
const env: NodeJS.ProcessEnv = { SHELL: "/bin/failing", PATH: "/usr/bin" };
// Explicit opt-in keeps real shell coverage independent of globally installed tools.
it.effect.runIf(process.env.T3_TEST_NU_PATH !== undefined)(
"hydrates the desktop through a renamed Nushell symlink with native export conversions",
diff --git a/apps/desktop/src/shell/DesktopShellEnvironment.ts b/apps/desktop/src/shell/DesktopShellEnvironment.ts
index 2b41b6ea7..dfe68ac88 100644
index 3fdec7932..1182af006 100644
--- a/apps/desktop/src/shell/DesktopShellEnvironment.ts
+++ b/apps/desktop/src/shell/DesktopShellEnvironment.ts
@@ -10,6 +10,7 @@ import * as Stream from "effect/Stream";
@@ -14,6 +14,7 @@ import * as Stream from "effect/Stream";
import * as ChildProcess from "effect/process/ChildProcess";
import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner";

+import { flatpakAppId } from "../app/Flatpak.ts";
import * as DesktopEnvironment from "../app/DesktopEnvironment.ts";

type EnvironmentPatch = Record<string, string>;
@@ -515,6 +516,7 @@ const installPosixEnvironment = Effect.fn("desktop.shellEnvironment.installPosix
const installShellEnvironment = (
config: ShellEnvironmentConfig,
): Effect.Effect<void, never, ChildProcessSpawner.ChildProcessSpawner | FileSystem.FileSystem> => {
+ if (flatpakAppId(config.env, config.platform) !== null) return Effect.void;
@@ -513,6 +514,7 @@ const installShellEnvironment = (
never,
ChildProcessSpawner.ChildProcessSpawner | FileSystem.FileSystem
> => {
+ if (flatpakAppId(config.env, config.platform) !== null) return Effect.succeed(false);
if (config.platform === "win32") {
return installWindowsEnvironment(config);
}
diff --git a/apps/desktop/src/updates/DesktopUpdates.test.ts b/apps/desktop/src/updates/DesktopUpdates.test.ts
index 26ad0f1c4..fc5f324df 100644
index 94bfbe44c..9a069527d 100644
--- a/apps/desktop/src/updates/DesktopUpdates.test.ts
+++ b/apps/desktop/src/updates/DesktopUpdates.test.ts
@@ -1,4 +1,4 @@
Expand All @@ -54,7 +54,7 @@ index 26ad0f1c4..fc5f324df 100644
import { DESKTOP_UPDATE_RESTART_MARKER_FILE } from "@t3tools/contracts";
import * as Cause from "effect/Cause";
import * as Deferred from "effect/Deferred";
@@ -21,6 +21,28 @@ import * as DesktopUpdates from "./DesktopUpdates.ts";
@@ -22,6 +22,28 @@ import * as DesktopUpdates from "./DesktopUpdates.ts";
import { flushCallbacks, makeHarness } from "./updatesTestHarness.ts";

describe("DesktopUpdates", () => {
Expand Down Expand Up @@ -84,15 +84,15 @@ index 26ad0f1c4..fc5f324df 100644
const cause = Cause.combine(
Cause.fail(new Error("updater failed")),
diff --git a/apps/desktop/src/updates/DesktopUpdates.ts b/apps/desktop/src/updates/DesktopUpdates.ts
index 77069d3ba..03ff73f2a 100644
index ae3c2588f..0c6557464 100644
--- a/apps/desktop/src/updates/DesktopUpdates.ts
+++ b/apps/desktop/src/updates/DesktopUpdates.ts
@@ -1,3 +1,4 @@
+import { flatpakAppId } from "../app/Flatpak.ts";
import {
DESKTOP_UPDATE_RESTART_MARKER_FILE,
DesktopUpdateChannelSchema,
@@ -321,6 +322,9 @@ function getAutoUpdateDisabledReason(args: {
@@ -316,6 +317,9 @@ function getAutoUpdateDisabledReason(args: {
disabledByEnv: boolean;
hasUpdateFeedConfig: boolean;
}): string | null {
Expand Down
5 changes: 3 additions & 2 deletions patches/0005-flatpak-build-artifact.patch
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
diff --git a/scripts/build-desktop-artifact.ts b/scripts/build-desktop-artifact.ts
index cb47e40b8..2d54e1ae7 100644
index d2cff4726..1de29fedc 100644
--- a/scripts/build-desktop-artifact.ts
+++ b/scripts/build-desktop-artifact.ts
@@ -3845,9 +3845,16 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* (
@@ -4114,10 +4114,17 @@ const buildDesktopArtifactUnlocked = Effect.fn("buildDesktopArtifactUnlocked")(f
for (const entry of stageEntries) {
const from = path.join(stageDistDir, entry);
const stat = yield* fs.stat(from).pipe(Effect.orElseSucceed(() => null));
Expand All @@ -21,3 +21,4 @@ index cb47e40b8..2d54e1ae7 100644
+ }
copiedArtifacts.push(to);
}

Loading