Skip to content

Commit 6a3fd0a

Browse files
authored
Merge branch 'main' into t3code/git-audit-stability
2 parents c4e2315 + e0f3abd commit 6a3fd0a

659 files changed

Lines changed: 38648 additions & 14979 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/release.yml

Lines changed: 148 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,68 @@ jobs:
244244
exit $code
245245
}
246246
247+
- name: Prepare Azure Trusted Signing
248+
if: matrix.platform == 'win'
249+
shell: pwsh
250+
env:
251+
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
252+
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
253+
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
254+
AZURE_TRUSTED_SIGNING_ENDPOINT: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }}
255+
AZURE_TRUSTED_SIGNING_ACCOUNT_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }}
256+
AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME }}
257+
AZURE_TRUSTED_SIGNING_PUBLISHER_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_PUBLISHER_NAME }}
258+
run: |
259+
$ErrorActionPreference = "Stop"
260+
261+
$requiredSecrets = @(
262+
$env:AZURE_TENANT_ID,
263+
$env:AZURE_CLIENT_ID,
264+
$env:AZURE_CLIENT_SECRET,
265+
$env:AZURE_TRUSTED_SIGNING_ENDPOINT,
266+
$env:AZURE_TRUSTED_SIGNING_ACCOUNT_NAME,
267+
$env:AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME,
268+
$env:AZURE_TRUSTED_SIGNING_PUBLISHER_NAME
269+
)
270+
if ($requiredSecrets | Where-Object { [string]::IsNullOrWhiteSpace($_) }) {
271+
Write-Host "Azure Trusted Signing disabled; skipping TrustedSigning module preparation."
272+
exit 0
273+
}
274+
275+
try {
276+
Install-PackageProvider `
277+
-Name NuGet `
278+
-MinimumVersion 2.8.5.201 `
279+
-Force `
280+
-Scope CurrentUser `
281+
-ErrorAction Stop
282+
} catch {
283+
Write-Warning "Could not bootstrap NuGet package provider. Continuing because the runner may already have a usable provider. $($_.Exception.Message)"
284+
}
285+
286+
Install-Module `
287+
-Name TrustedSigning `
288+
-MinimumVersion 0.5.0 `
289+
-Force `
290+
-AllowClobber `
291+
-Repository PSGallery `
292+
-Scope CurrentUser `
293+
-ErrorAction Stop
294+
295+
Import-Module TrustedSigning -MinimumVersion 0.5.0 -Force
296+
Get-Command Invoke-TrustedSigning -ErrorAction Stop
297+
298+
$moduleRoots = @(
299+
[System.IO.Path]::Combine([Environment]::GetFolderPath("MyDocuments"), "PowerShell", "Modules"),
300+
[System.IO.Path]::Combine([Environment]::GetFolderPath("MyDocuments"), "WindowsPowerShell", "Modules"),
301+
[System.IO.Path]::Combine($env:ProgramFiles, "PowerShell", "Modules"),
302+
[System.IO.Path]::Combine($env:ProgramFiles, "WindowsPowerShell", "Modules")
303+
)
304+
$modulePathEntries = @($moduleRoots + ($env:PSModulePath -split ";")) |
305+
Where-Object { $_ -and (Test-Path $_) } |
306+
Select-Object -Unique
307+
"PSModulePath=$($modulePathEntries -join ';')" >> $env:GITHUB_ENV
308+
247309
- name: Build desktop artifact
248310
shell: bash
249311
env:
@@ -511,6 +573,90 @@ jobs:
511573
fail_on_unmatched_files: true
512574
token: ${{ steps.app_token.outputs.token }}
513575

576+
deploy_web:
577+
name: Deploy hosted web app
578+
needs: [preflight, release]
579+
if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.release.result == 'success' }}
580+
runs-on: blacksmith-8vcpu-ubuntu-2404
581+
timeout-minutes: 10
582+
env:
583+
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
584+
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
585+
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
586+
T3CODE_WEB_ROUTER_URL: ${{ vars.T3CODE_WEB_ROUTER_URL }}
587+
T3CODE_WEB_LATEST_DOMAIN: ${{ vars.T3CODE_WEB_LATEST_DOMAIN }}
588+
T3CODE_WEB_NIGHTLY_DOMAIN: ${{ vars.T3CODE_WEB_NIGHTLY_DOMAIN }}
589+
VERCEL_TEAM_SLUG: ${{ vars.VERCEL_TEAM_SLUG }}
590+
steps:
591+
- name: Checkout
592+
uses: actions/checkout@v6
593+
with:
594+
ref: ${{ needs.preflight.outputs.ref }}
595+
596+
- name: Setup Bun
597+
uses: oven-sh/setup-bun@v2
598+
with:
599+
bun-version-file: package.json
600+
601+
- name: Setup Node
602+
uses: actions/setup-node@v6
603+
with:
604+
node-version-file: package.json
605+
606+
- name: Install release tooling dependencies
607+
run: bun install --frozen-lockfile --filter=@t3tools/scripts --filter=@t3tools/web
608+
609+
- name: Align package versions to release version
610+
run: node scripts/update-release-package-versions.ts "${{ needs.preflight.outputs.version }}"
611+
612+
- name: Refresh release lockfile
613+
run: bun install --lockfile-only --ignore-scripts
614+
615+
- name: Deploy and alias channel
616+
shell: bash
617+
run: |
618+
set -euo pipefail
619+
620+
if [[ -z "${VERCEL_TOKEN:-}" || -z "${VERCEL_ORG_ID:-}" || -z "${VERCEL_PROJECT_ID:-}" ]]; then
621+
echo "Missing one or more required Vercel secrets: VERCEL_TOKEN, VERCEL_ORG_ID, VERCEL_PROJECT_ID." >&2
622+
exit 1
623+
fi
624+
625+
router_url="${T3CODE_WEB_ROUTER_URL:-https://app.t3.codes}"
626+
latest_domain="${T3CODE_WEB_LATEST_DOMAIN:-latest.app.t3.codes}"
627+
nightly_domain="${T3CODE_WEB_NIGHTLY_DOMAIN:-nightly.app.t3.codes}"
628+
629+
if [[ "${{ needs.preflight.outputs.release_channel }}" == "stable" ]]; then
630+
channel_domain="$latest_domain"
631+
channel_name="latest"
632+
else
633+
channel_domain="$nightly_domain"
634+
channel_name="nightly"
635+
fi
636+
637+
vercel_scope_args=()
638+
if [[ -n "${VERCEL_TEAM_SLUG:-}" ]]; then
639+
vercel_scope_args=(--scope "$VERCEL_TEAM_SLUG")
640+
fi
641+
642+
echo "Deploying hosted web app for $channel_name channel."
643+
deployment_url="$(
644+
bunx vercel@53.1.1 deploy apps/web \
645+
--prod \
646+
--skip-domain \
647+
--yes \
648+
--token "$VERCEL_TOKEN" \
649+
"${vercel_scope_args[@]}" \
650+
--build-env "APP_VERSION=${{ needs.preflight.outputs.version }}" \
651+
--build-env "VITE_HOSTED_APP_URL=$router_url" \
652+
--build-env "VITE_HOSTED_APP_CHANNEL=$channel_name"
653+
)"
654+
655+
echo "Aliasing $deployment_url to $channel_domain."
656+
bunx vercel@53.1.1 alias set "$deployment_url" "$channel_domain" \
657+
--token "$VERCEL_TOKEN" \
658+
"${vercel_scope_args[@]}"
659+
514660
finalize:
515661
name: Finalize release
516662
if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.release.result == 'success' && needs.preflight.outputs.release_channel == 'stable' }}
@@ -595,8 +741,9 @@ jobs:
595741
always() && !cancelled() &&
596742
needs.preflight.result == 'success' &&
597743
needs.release.result == 'success' &&
744+
needs.deploy_web.result == 'success' &&
598745
(needs.finalize.result == 'success' || needs.finalize.result == 'skipped')
599-
needs: [preflight, release, finalize]
746+
needs: [preflight, release, deploy_web, finalize]
600747
runs-on: blacksmith-8vcpu-ubuntu-2404
601748
timeout-minutes: 10
602749
steps:

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,5 @@ __screenshots__/
2323
.tanstack
2424
squashfs-root/
2525
.vercel
26+
dist-electron/
27+
.electron-runtime/

.oxlintrc.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"**/routeTree.gen.ts"
1010
],
1111
"plugins": ["eslint", "oxc", "react", "unicorn", "typescript"],
12+
"jsPlugins": ["./oxlint-plugin-t3code/index.ts"],
1213
"categories": {
1314
"correctness": "warn",
1415
"suspicious": "warn",
@@ -17,6 +18,8 @@
1718
"rules": {
1819
"react-in-jsx-scope": "off",
1920
"eslint/no-shadow": "off",
20-
"eslint/no-await-in-loop": "off"
21+
"eslint/no-await-in-loop": "off",
22+
"eslint/no-underscore-dangle": "off",
23+
"t3code/no-inline-schema-compile": "warn"
2124
}
2225
}

.plans/19-remote-endpoints-hosted-static.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,4 +347,3 @@ Each implementation PR should run:
347347
- `bun typecheck`
348348
- focused tests for changed backend/web behavior
349349
- backend tests for any server-side endpoint discovery or auth changes using `bun run test`, never `bun test`
350-

REMOTE.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,43 @@ After setup, the renderer connects to a local forwarded HTTP/WebSocket endpoint.
115115

116116
SSH launch is a desktop feature because it needs local process and SSH access. Once the environment is paired and saved, it uses the same environment list and connection model as direct LAN, Tailscale, HTTPS, or future tunnel-backed environments.
117117

118+
#### SSH Launch Troubleshooting
119+
120+
The desktop SSH launcher connects with a non-interactive `sh` session, writes a small launcher script under `~/.t3/ssh-launch/<host-key>/`, starts or reuses a remote T3 server, and forwards the remote loopback port back to your desktop.
121+
122+
The remote host must have a compatible Node.js runtime. T3 Code uses the server package's `engines.node` requirement:
123+
124+
```text
125+
^22.16 || ^23.11 || >=24.10
126+
```
127+
128+
During SSH launch, T3 Code first checks whether `node` is already available on `PATH`. If it is missing, the launcher tries common non-interactive shell locations and version-manager shims/activation hooks:
129+
130+
- `~/.local/bin`, `~/bin`, `/opt/homebrew/bin`, `/usr/local/bin`, `/usr/bin`, `/bin`
131+
- Volta via `~/.volta/bin`
132+
- asdf via `~/.asdf/shims`, `~/.asdf/bin`, or `~/.asdf/asdf.sh`
133+
- mise via `~/.local/share/mise/shims`, `~/.mise/shims`, or `mise activate sh`
134+
- fnm via `fnm env --use-on-cd --shell sh` or `fnm env --shell sh`
135+
- nodenv via `~/.nodenv/bin`, `~/.nodenv/shims`, or `nodenv init -`
136+
- nvm via `$NVM_DIR/nvm.sh`, then `nvm use default`, `nvm use node`, or `nvm use --lts`
137+
- installed nvm versions under `$NVM_DIR/versions/node/*/bin`
138+
139+
If launch fails with `node: command not found`, a port-scan failure, or a message that the remote Node version does not satisfy the required range, SSH into the host and check the same non-interactive shell path T3 Code uses:
140+
141+
```bash
142+
ssh user@example.com 'sh -lc "command -v node && node --version"'
143+
```
144+
145+
If that does not print a compatible Node version, configure your version manager for non-interactive shells or install a compatible Node binary in one of the searched locations. For example, with nvm you may need a default alias:
146+
147+
```bash
148+
nvm alias default 24
149+
```
150+
151+
With mise/asdf/fnm/nodenv, make sure the tool's shim directory is installed and points at a Node version satisfying the range above.
152+
153+
If reconnecting after an app update fails, retry the SSH launch once. The launcher now compares its generated runner script, stops stale launcher-managed remote servers, clears the SSH launch PID/port state, and starts a fresh remote server. You should not normally need to delete `~/.t3/ssh-launch` or kill `t3` processes manually.
154+
118155
## How Pairing Works
119156

120157
The remote device does not need a long-lived secret up front.

apps/desktop/.gitignore

Lines changed: 0 additions & 2 deletions
This file was deleted.

apps/desktop/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
"dependencies": {
1818
"@effect/platform-node": "catalog:",
1919
"effect": "catalog:",
20-
"electron": "40.9.3",
20+
"electron": "41.5.0",
2121
"electron-updater": "^6.6.2"
2222
},
2323
"devDependencies": {
24+
"@effect/language-service": "catalog:",
25+
"@effect/vitest": "catalog:",
2426
"@t3tools/client-runtime": "workspace:*",
2527
"@t3tools/contracts": "workspace:*",
2628
"@t3tools/shared": "workspace:*",

0 commit comments

Comments
 (0)