Skip to content

Commit 25e0090

Browse files
authored
chore: sync with upstream pingdotgg/t3code main (#61)
2 parents 897909f + a7473cf commit 25e0090

512 files changed

Lines changed: 56986 additions & 24783 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/deploy-relay.yml‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ on:
44
push:
55
branches:
66
- main
7+
# Alchemy does not redeploy the Worker when only a Config value read in its
8+
# Init changes (alchemy-run/alchemy#1831), so a changed repository variable
9+
# needs a forced deploy to reach production.
10+
workflow_dispatch:
11+
inputs:
12+
force:
13+
description: Redeploy every resource, including ones with no detected changes
14+
type: boolean
15+
default: true
716

817
permissions:
918
contents: read
@@ -17,6 +26,8 @@ concurrency:
1726
jobs:
1827
deploy_relay:
1928
name: Deploy production relay
29+
# A manual run from another branch would deploy that branch to production.
30+
if: github.ref == 'refs/heads/main'
2031
runs-on: ubuntu-24.04 # blacksmith-8vcpu-ubuntu-2404
2132
timeout-minutes: 15
2233
environment:
@@ -28,6 +39,7 @@ jobs:
2839
RELAY_DOMAIN: ${{ vars.RELAY_DOMAIN }}
2940
RELAY_API_ZONE_NAME: ${{ vars.RELAY_API_ZONE_NAME }}
3041
RELAY_TUNNEL_ZONE_NAME: ${{ vars.RELAY_TUNNEL_ZONE_NAME }}
42+
RELAY_TUNNEL_CLEANUP_MODE: ${{ vars.RELAY_TUNNEL_CLEANUP_MODE }}
3143
CLERK_PUBLISHABLE_KEY: ${{ vars.CLERK_PUBLISHABLE_KEY }}
3244
CLERK_JWT_AUDIENCE: ${{ vars.CLERK_JWT_AUDIENCE }}
3345
APNS_ENVIRONMENT: ${{ vars.APNS_ENVIRONMENT }}
@@ -70,7 +82,7 @@ jobs:
7082
7183
- name: Deploy production relay stage
7284
if: steps.creds.outputs.configured == 'true'
73-
run: vp run --filter t3code-relay deploy --stage prod --yes --no-input
85+
run: vp run --filter t3code-relay deploy --stage prod --yes --no-input ${{ inputs.force && '--force' || '' }}
7486
env:
7587
# The PublishClientConfig action writes the client env here instead
7688
# of the repo-root .env; nothing on the runner reads it.

‎.github/workflows/release-desktop.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,7 @@ jobs:
510510
"release/*.dmg"
511511
"release/*.zip"
512512
"release/*.AppImage"
513+
"release/*.deb"
513514
"release/*.exe"
514515
)
515516
# Preview builds have no publish config, so electron-builder writes

‎.github/workflows/release.yml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,7 @@ jobs:
640640
clerk_cli_oauth_client_id: ${{ needs.relay_public_config.outputs.clerk_cli_oauth_client_id }}
641641
relay_url: ${{ needs.relay_public_config.outputs.relay_url }}
642642
label: Linux arm64
643-
runner: ubuntu-24.04-arm
643+
runner: blacksmith-16vcpu-ubuntu-2404-arm
644644
platform: linux
645645
target: AppImage
646646
arch: arm64
@@ -890,6 +890,7 @@ jobs:
890890
echo 'release-assets/*.dmg'
891891
echo 'release-assets/*.zip'
892892
echo 'release-assets/*.AppImage'
893+
echo 'release-assets/*.deb'
893894
echo 'release-assets/*.exe'
894895
if [[ "${{ needs.preflight.outputs.release_channel }}" != "preview" ]]; then
895896
echo 'release-assets/*.blockmap'

‎README.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ winget install T3Tools.T3Code
5454
brew install --cask t3-code
5555
```
5656

57+
#### Debian, Ubuntu (`.deb`)
58+
59+
Download the `.deb` from [GitHub Releases](https://github.com/pingdotgg/t3code/releases), then:
60+
61+
```bash
62+
sudo apt install ./T3-Code-*.deb
63+
```
64+
5765
#### Arch Linux (AUR)
5866

5967
Stable:

‎apps/desktop/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"version": "0.0.42",
44
"private": true,
55
"type": "module",
6-
"main": "dist-electron/main.cjs",
6+
"main": "dist-electron/boot.cjs",
77
"scripts": {
88
"ensure:electron": "node scripts/ensure-electron-runtime.mjs",
99
"start": "node scripts/start-electron.mjs",

‎apps/desktop/scripts/main-process-bundle.test.mjs‎

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import * as NodeChildProcess from "node:child_process";
12
import * as NodeFSP from "node:fs/promises";
23
import * as NodeOS from "node:os";
34
import * as NodePath from "node:path";
@@ -108,3 +109,65 @@ void import("./linux.ts").then(({ result }) => process.emit("ready", result));`,
108109
await NodeFSP.rm(directory, { recursive: true, force: true });
109110
}
110111
});
112+
113+
it("loads the emitted packaged boot entry and backend cache preload", async () => {
114+
const directory = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "t3-desktop-boot-"));
115+
try {
116+
const entries = ["src/boot.ts", "src/compileCache.ts"];
117+
await NodeFSP.mkdir(NodePath.join(directory, "src"));
118+
await Promise.all(
119+
entries.map((entry) =>
120+
NodeFSP.copyFile(new URL(`../${entry}`, import.meta.url), NodePath.join(directory, entry)),
121+
),
122+
);
123+
assert.ok(Array.isArray(desktopConfig.pack));
124+
for (const packConfig of desktopConfig.pack) {
125+
if (!Array.isArray(packConfig.entry)) continue;
126+
if (!packConfig.entry.some((entry) => entries.includes(entry))) continue;
127+
await build({
128+
...packConfig,
129+
config: false,
130+
cwd: directory,
131+
tsconfig: false,
132+
sourcemap: false,
133+
onSuccess: undefined,
134+
logLevel: "silent",
135+
});
136+
}
137+
const outputDirectory = NodePath.join(directory, "dist-electron");
138+
const fixture = `console.log(require('node:module').getCompileCacheDir() ? 'cached' : 'uncached');`;
139+
await NodeFSP.writeFile(NodePath.join(outputDirectory, "main.cjs"), fixture);
140+
await NodeFSP.writeFile(
141+
NodePath.join(outputDirectory, "backend.mjs"),
142+
`import { getCompileCacheDir } from 'node:module'; console.log(getCompileCacheDir() ? 'cached' : 'uncached');`,
143+
);
144+
for (const disabled of [false, true]) {
145+
for (const args of [
146+
[NodePath.join(outputDirectory, "boot.cjs")],
147+
[
148+
"--require",
149+
NodePath.join(outputDirectory, "compileCache.cjs"),
150+
NodePath.join(outputDirectory, "backend.mjs"),
151+
],
152+
]) {
153+
const child = NodeChildProcess.spawnSync(process.execPath, args, {
154+
encoding: "utf8",
155+
env: {
156+
...process.env,
157+
APPIMAGE: "",
158+
NODE_COMPILE_CACHE: undefined,
159+
NODE_DISABLE_COMPILE_CACHE: disabled ? "1" : undefined,
160+
XDG_CACHE_HOME: directory,
161+
TMPDIR: directory,
162+
TEMP: directory,
163+
TMP: directory,
164+
},
165+
});
166+
assert.equal(child.status, 0, child.stderr);
167+
assert.equal(child.stdout.trim(), disabled ? "uncached" : "cached");
168+
}
169+
}
170+
} finally {
171+
await NodeFSP.rm(directory, { recursive: true, force: true });
172+
}
173+
});

‎apps/desktop/scripts/smoke-test.mjs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { resolveElectronLaunchCommand } from "./electron-launcher.mjs";
55

66
const __dirname = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url));
77
const desktopDir = NodePath.resolve(__dirname, "..");
8-
const mainJs = NodePath.resolve(desktopDir, "dist-electron/main.cjs");
8+
const mainJs = NodePath.resolve(desktopDir, "dist-electron/boot.cjs");
99

1010
console.log("\nLaunching Electron smoke test...");
1111

‎apps/desktop/src/app/DesktopAppActivation.test.ts‎

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,25 @@ function request(requestId: string, platform: NodeJS.Platform): DesktopAppActiva
4444
};
4545
}
4646

47+
function startOkServer(target: ReturnType<typeof makeTarget>, userId: number | undefined) {
48+
return startDesktopAppControlServer({
49+
...target,
50+
userId,
51+
handle: async (input) => ({
52+
version: 1,
53+
requestId: input.requestId,
54+
ok: true,
55+
projectId: ProjectId.make("project-1"),
56+
threadId: ThreadId.make("thread-1"),
57+
}),
58+
cancel: () => undefined,
59+
onReclaimError: () => undefined,
60+
}).then((server) => {
61+
openServers.push(server);
62+
return server;
63+
});
64+
}
65+
4766
function exchange(address: string, payload: DesktopAppActivationRequest) {
4867
return new Promise<DesktopAppActivationResponse>((resolve, reject) => {
4968
const socket = NodeNet.createConnection(address);
@@ -84,6 +103,7 @@ describe("desktop app control server", () => {
84103
};
85104
},
86105
cancel: () => undefined,
106+
onReclaimError: () => undefined,
87107
});
88108
openServers.push(server);
89109

@@ -117,6 +137,7 @@ describe("desktop app control server", () => {
117137
userId,
118138
handle: () => new Promise(() => undefined),
119139
cancel: resolveCanceled,
140+
onReclaimError: () => undefined,
120141
});
121142
openServers.push(server);
122143
const socket = NodeNet.createConnection(target.address);
@@ -137,4 +158,50 @@ describe("desktop app control server", () => {
137158
});
138159
}),
139160
);
161+
162+
// Two desktop apps can share one state dir, such as nightly and a preview build.
163+
it.effect("keeps a newer app's socket when an older app on the same state dir quits", () =>
164+
Effect.gen(function* () {
165+
const platform = yield* HostProcessPlatform;
166+
const userId = yield* HostProcessUserId;
167+
if (platform === "win32") return;
168+
yield* Effect.promise(async () => {
169+
const root = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "t3-app-takeover-test-"));
170+
const target = makeTarget(NodePath.join(root, "userdata"), platform, userId);
171+
const older = await startOkServer(target, userId);
172+
await startOkServer(target, userId);
173+
174+
await older.close();
175+
176+
await expect(
177+
exchange(target.address, request("after-quit", platform)),
178+
).resolves.toMatchObject({ ok: true, requestId: "after-quit" });
179+
await NodeFSP.rm(root, { recursive: true, force: true });
180+
});
181+
}),
182+
);
183+
184+
it.effect("binds its address again after the socket file is removed", () =>
185+
Effect.gen(function* () {
186+
const platform = yield* HostProcessPlatform;
187+
const userId = yield* HostProcessUserId;
188+
if (platform === "win32") return;
189+
yield* Effect.promise(async () => {
190+
const root = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "t3-app-reclaim-test-"));
191+
const target = makeTarget(NodePath.join(root, "userdata"), platform, userId);
192+
const server = await startOkServer(target, userId);
193+
194+
await NodeFSP.unlink(target.address);
195+
await server.reclaim();
196+
197+
await expect(
198+
exchange(target.address, request("reclaimed", platform)),
199+
).resolves.toMatchObject({
200+
ok: true,
201+
requestId: "reclaimed",
202+
});
203+
await NodeFSP.rm(root, { recursive: true, force: true });
204+
});
205+
}),
206+
);
140207
});

0 commit comments

Comments
 (0)