Skip to content

Commit 52c7dcd

Browse files
merge main into fix/issue-23151
2 parents ff91e40 + 8f26ed7 commit 52c7dcd

2,474 files changed

Lines changed: 172465 additions & 32497 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/scripts/ci-changes.cjs‎

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,24 @@
1-
// Decides which CI jobs a change needs. Prints `key=value` lines for
2-
// $GITHUB_OUTPUT; a job runs unless its key is "false".
3-
//
4-
// node ci-changes.cjs --paths <file> gate by the listed paths
5-
// node ci-changes.cjs --paths <file> --full run every Linux job, gate only the macOS lint
6-
// node ci-changes.cjs changed paths unknown: run everything
7-
//
8-
// scripts/check-changed.ts requires the same decisions so local runs match CI.
9-
//
10-
// Every changed path maps to workspace packages, which then pull in their
11-
// dependents from package.json. A path this file does not recognize runs
12-
// everything, so a new root file or directory can only cost minutes.
1+
// Unknown paths run every job; recognized packages pull in their dependents.
2+
// scripts/check-changed.ts shares these decisions so local checks match CI.
133
const fs = require("node:fs");
144
const path = require("node:path");
155

166
const root = path.resolve(__dirname, "../..");
177

18-
// Never read by a build, typecheck, or test. Lint still formats them.
198
const INERT = [
209
/^docs\//,
2110
/^\.(agents|cursor|vscode|devcontainer|macroscope|repos)\//,
2211
/^[^/]+\.md$/,
2312
/^LICENSE$/,
2413
];
2514

26-
// Run everything: the root vite.config.ts loads the first as setup for every
27-
// package's tests, and the vendored mobile tarballs are installed dependencies.
15+
// Shared test setup and vendored mobile dependencies affect every package.
2816
const RUNS_EVERYTHING = [
2917
/^packages\/shared\/src\/(HostProcess|testing\/longTempDir)\.ts$/,
3018
/^apps\/mobile\/deps\//,
3119
];
3220

33-
// Paths outside a package, or files inside one that other packages read
34-
// through relative imports rather than a package.json dependency.
21+
// Includes cross-package reads absent from package.json dependencies.
3522
const EXTRA_OWNERS = [
3623
// Vite configs and tsconfigs of these apps import scripts/lib directly.
3724
[/^scripts\/lib\//, ["@t3tools/desktop", "@t3tools/mobile", "@t3tools/web", "t3"]],
@@ -43,9 +30,7 @@ const EXTRA_OWNERS = [
4330
];
4431
const RUST = /^native\/(resource-monitor|kde-snap-shot|hyprland-snap-shot)\//;
4532

46-
// Tests that read another package's files without declaring it. The scripts
47-
// tests read app manifests, assets, and sources (cursor-sdk-packaging,
48-
// third-party-licenses, brand-assets, build-desktop-artifact).
33+
// These tests read other packages without declaring dependencies.
4934
const TEST_ONLY_DEPS = {
5035
"@t3tools/provider-pi": ["@t3tools/client-runtime"],
5136
"@t3tools/scripts": [
@@ -56,17 +41,12 @@ const TEST_ONLY_DEPS = {
5641
"@t3tools/provider-cursor",
5742
],
5843
};
59-
// Declared dependencies that only feed a build step: the server bundles the
60-
// built web app (apps/server/vite.config.ts) but never imports it.
44+
// The server bundles the web build without importing it.
6145
const BUILD_ONLY_DEPS = { t3: ["@t3tools/web"] };
6246

63-
// Sources scripts/mobile-native-static-check.ts lints, plus the tool and rule
64-
// configuration that decides how it lints them, plus the root package.json
65-
// that defines the lint:mobile command.
6647
const MOBILE_NATIVE =
6748
/^apps\/mobile\/.*\.(swift|kt|kts)$|^apps\/mobile\/(\.swiftlint\.yml|detekt\.yml|\.editorconfig|Brewfile)$|^scripts\/mobile-native-static-check\.ts$|^package\.json$|^\.github\/workflows\/ci\.yml$|^\.github\/scripts\/ci-changes\.cjs$|^\.github\/actions\/ci-changes\//;
6849

69-
// Jobs that test every package except these, which have jobs of their own.
7050
const OWN_TEST_JOB = new Set(["t3", "@t3tools/web", "@t3tools/monorepo"]);
7151

7252
function readWorkspace() {
@@ -108,8 +88,7 @@ function readWorkspace() {
10888
return packages;
10989
}
11090

111-
// Returns the packages a path touches, [] for an inert path, or null when the
112-
// path is not understood and everything must run.
91+
// [] means inert; null means unknown and requires every job.
11392
function ownersOf(file, packages) {
11493
if (INERT.some((pattern) => pattern.test(file))) return [];
11594
// A manifest can change dependencies or versions other packages read.
@@ -148,7 +127,6 @@ function decide(files, packages, full) {
148127
if (everything) {
149128
affected = all;
150129
} else {
151-
// Add dependents until nothing changes.
152130
let grew = true;
153131
while (grew) {
154132
grew = false;

‎.github/scripts/mirror-play-apk.cjs‎

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
// Publishes closed-testing betas, or downloads the internal APK for a normal
2-
// nightly release. Play generates and signs the APK from the
3-
// uploaded bundle with the app signing key, so it carries the same signature
4-
// as the Play Store install. Run by .github/workflows/mobile-android-apk.yml.
1+
// Play signs generated APKs with the app signing key, matching Play Store installs.
52
const { execFileSync } = require("node:child_process");
63
const crypto = require("node:crypto");
74
const fs = require("node:fs");
@@ -50,10 +47,7 @@ async function play(token, method, url) {
5047
return response;
5148
}
5249

53-
// Highest version code the track serves, with its release name. Play names a
54-
// release after the bundle's versionName unless the uploader sets one. This
55-
// endpoint reads without opening an edit; an edit would invalidate any open
56-
// EAS Submit edit made with the same service account.
50+
// Opening an edit would invalidate any EAS Submit edit using the same service account.
5751
async function newestTrackBuild(token, appUrl, trackName) {
5852
const { releases = [] } = await (
5953
await play(token, "GET", `${appUrl}/tracks/${trackName}/releases`)
@@ -82,8 +76,7 @@ function readRelease(tag) {
8276
}
8377
}
8478

85-
// Download to a temporary directory so a failed request cannot leave a partial
86-
// APK among the release assets.
79+
// Stage downloads so failed requests cannot leave partial release assets.
8780
async function downloadApk({ token, appUrl, build, apkName }) {
8881
const generated = await (
8982
await play(token, "GET", `${appUrl}/generatedApks/${build.versionCode}`)

‎.github/scripts/stage-preview-bundle.py‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@
1212
"server/dist/client/index.html",
1313
"desktop/dist-electron/main.cjs",
1414
}
15-
# The current bundle is about 32 MiB compressed. Bound extraction on the
16-
# trusted runner even when the PR replaces the uploader entirely.
15+
# Bound trusted-runner extraction even when a PR replaces the uploader.
1716
MAX_ARCHIVE_BYTES = 512 * 1024 * 1024
1817
MAX_EXPANDED_BYTES = 2 * 1024 * 1024 * 1024
1918
MAX_ENTRIES = 50_000
@@ -58,9 +57,7 @@ def stage_bundle(archive: Path, destination: Path):
5857
files.add(name)
5958
if not REQUIRED_FILES <= files:
6059
raise ValueError("Preview bundle is missing required entry points")
61-
# Validate all names before writing anything. This is a fresh directory
62-
# outside the checkout; neither pre-existing links nor trusted files
63-
# can be followed or overwritten. ZIP permissions are never restored.
60+
# Validate before writing into a fresh directory so existing links/files cannot be followed or overwritten.
6461
destination.mkdir(parents=True, exist_ok=False)
6562
for entry in entries:
6663
target = destination / entry.filename

‎.github/scripts/thread-transfer-report.cjs‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,7 @@ async function resolve({ github, context, core }) {
294294

295295
const sourceArtifacts = await artifactsForRun(github, owner, repo, source.id);
296296
const sourceArtifact = findResultArtifact(sourceArtifacts);
297-
// CI requires the artifact whenever the server tests run, so a passing run
298-
// without one skipped them because the PR cannot affect the server.
297+
// Successful server tests require this artifact; its absence means CI skipped them.
299298
if (!sourceArtifact && source.conclusion === "success") {
300299
core.info("CI skipped the server tests for this change; nothing to report.");
301300
core.setOutput("publish", "false");
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
name: Clean up relay preview
2+
3+
# Tears down stage `pr-<n>` when `relay:preview` is removed or the PR closes,
4+
# and retries teardown for closed labeled PRs on a schedule. A label stays on a
5+
# PR only while its teardown is outstanding, so the scheduled retry lists
6+
# exactly the failures.
7+
#
8+
# This runs under pull_request_target, so the workflow and the Alchemy program
9+
# come from the default branch. No PR code runs here, which is why it needs no
10+
# reviewer approval and why a PR cannot edit its own teardown to reach secrets.
11+
on:
12+
pull_request_target:
13+
types: [unlabeled, closed]
14+
schedule:
15+
- cron: "17 7 * * *"
16+
workflow_dispatch:
17+
18+
permissions:
19+
contents: read
20+
issues: read
21+
pull-requests: read
22+
23+
jobs:
24+
cleanup_pr:
25+
name: Tear down relay preview
26+
if: >-
27+
github.ref == 'refs/heads/main' &&
28+
github.event_name == 'pull_request_target' &&
29+
(
30+
(github.event.action == 'unlabeled' && github.event.label.name == 'relay:preview') ||
31+
(github.event.action == 'closed' && contains(github.event.pull_request.labels.*.name, 'relay:preview'))
32+
)
33+
# Job-level so that events which skip this job, such as a closed PR without
34+
# the label, never take the group's pending slot. GitHub replaces a pending
35+
# run in a group with a newer one, which would drop a queued teardown.
36+
concurrency:
37+
group: relay-preview-${{ github.event.pull_request.number }}
38+
cancel-in-progress: false
39+
permissions:
40+
contents: read
41+
issues: write
42+
pull-requests: write
43+
runs-on: blacksmith-8vcpu-ubuntu-2404
44+
timeout-minutes: 30
45+
environment:
46+
name: relay-preview-cleanup
47+
env:
48+
GH_TOKEN: ${{ github.token }}
49+
STAGE: pr-${{ github.event.pull_request.number }}
50+
CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
51+
PLANETSCALE_ORGANIZATION: ${{ vars.PLANETSCALE_ORGANIZATION }}
52+
AXIOM_ORG_ID: ${{ vars.AXIOM_ORG_ID }}
53+
RELAY_API_ZONE_NAME: ${{ vars.RELAY_API_ZONE_NAME }}
54+
RELAY_TUNNEL_ZONE_NAME: ${{ vars.RELAY_TUNNEL_ZONE_NAME }}
55+
CLERK_PUBLISHABLE_KEY: ${{ vars.CLERK_PUBLISHABLE_KEY }}
56+
CLERK_JWT_AUDIENCE: ${{ vars.CLERK_JWT_AUDIENCE }}
57+
APNS_ENABLED: "false"
58+
ALCHEMY_TELEMETRY_DISABLED: "1"
59+
steps:
60+
- name: Checkout default branch
61+
uses: actions/checkout@v6
62+
with:
63+
ref: ${{ github.event.repository.default_branch }}
64+
sparse-checkout: |
65+
/*
66+
!/.repos/
67+
sparse-checkout-cone-mode: false
68+
69+
- name: Setup Vite+
70+
uses: voidzero-dev/setup-vp@v1
71+
with:
72+
node-version-file: package.json
73+
cache: true
74+
run-install: |
75+
args:
76+
- --filter=t3code-relay...
77+
78+
# Destroy also runs the preview tunnel sweep, which is part of the stack.
79+
- name: Destroy preview stage
80+
run: vp run --filter t3code-relay destroy --stage "$STAGE" --yes --no-input
81+
env:
82+
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
83+
PLANETSCALE_API_TOKEN_ID: ${{ secrets.PLANETSCALE_API_TOKEN_ID }}
84+
PLANETSCALE_API_TOKEN: ${{ secrets.PLANETSCALE_API_TOKEN }}
85+
AXIOM_TOKEN: ${{ secrets.AXIOM_TOKEN }}
86+
CLERK_SECRET_KEY: ${{ secrets.CLERK_SECRET_KEY }}
87+
88+
# Only reached after a successful destroy. A failed one keeps the label,
89+
# so the scheduled retry picks the PR up again.
90+
- name: Clear the teardown marker
91+
if: github.event.action == 'closed'
92+
run: gh pr edit "${{ github.event.pull_request.number }}" --repo "$GITHUB_REPOSITORY" --remove-label relay:preview
93+
94+
# An unlabeled event has already removed the label, so a failed teardown
95+
# must put it back or no retry will ever see the stage. Labels added with
96+
# GITHUB_TOKEN do not start new runs, so this does not redeploy.
97+
- name: Restore the teardown marker after a failed destroy
98+
if: failure() && github.event.action == 'unlabeled'
99+
run: gh pr edit "${{ github.event.pull_request.number }}" --repo "$GITHUB_REPOSITORY" --add-label relay:preview
100+
101+
list_closed_previews:
102+
name: List closed relay previews
103+
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request_target'
104+
runs-on: blacksmith-8vcpu-ubuntu-2404
105+
timeout-minutes: 10
106+
outputs:
107+
numbers: ${{ steps.list.outputs.numbers }}
108+
steps:
109+
# The issues endpoint is paginated by gh, so there is no listing cap. Pull
110+
# requests appear there with a `pull_request` key.
111+
- name: List closed PRs labeled relay:preview
112+
id: list
113+
env:
114+
GH_TOKEN: ${{ github.token }}
115+
run: |
116+
numbers=$(gh api --paginate --method GET "repos/$GITHUB_REPOSITORY/issues" \
117+
-f labels=relay:preview -f state=closed -f per_page=100 \
118+
--jq '.[] | select(.pull_request) | .number') \
119+
|| { echo "Could not list closed relay:preview PRs" >&2; exit 1; }
120+
json=$(printf '%s\n' "$numbers" | jq -Rsc 'split("\n") | map(select(length > 0) | tonumber)')
121+
echo "Closed relay:preview PRs: $json"
122+
echo "numbers=$json" >> "$GITHUB_OUTPUT"
123+
124+
sweep_pr:
125+
name: Retry teardown for PR ${{ matrix.number }}
126+
needs: list_closed_previews
127+
if: github.ref == 'refs/heads/main' && needs.list_closed_previews.outputs.numbers != '[]'
128+
runs-on: blacksmith-8vcpu-ubuntu-2404
129+
timeout-minutes: 30
130+
strategy:
131+
fail-fast: false
132+
max-parallel: 4
133+
matrix:
134+
number: ${{ fromJSON(needs.list_closed_previews.outputs.numbers) }}
135+
# Same group as the deploy and teardown for this PR, so a reopened PR's
136+
# deploy cannot run concurrently with a teardown of its previous stage.
137+
concurrency:
138+
group: relay-preview-${{ matrix.number }}
139+
cancel-in-progress: false
140+
permissions:
141+
contents: read
142+
issues: write
143+
pull-requests: write
144+
environment:
145+
name: relay-preview-cleanup
146+
env:
147+
GH_TOKEN: ${{ github.token }}
148+
STAGE: pr-${{ matrix.number }}
149+
CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
150+
PLANETSCALE_ORGANIZATION: ${{ vars.PLANETSCALE_ORGANIZATION }}
151+
AXIOM_ORG_ID: ${{ vars.AXIOM_ORG_ID }}
152+
RELAY_API_ZONE_NAME: ${{ vars.RELAY_API_ZONE_NAME }}
153+
RELAY_TUNNEL_ZONE_NAME: ${{ vars.RELAY_TUNNEL_ZONE_NAME }}
154+
CLERK_PUBLISHABLE_KEY: ${{ vars.CLERK_PUBLISHABLE_KEY }}
155+
CLERK_JWT_AUDIENCE: ${{ vars.CLERK_JWT_AUDIENCE }}
156+
APNS_ENABLED: "false"
157+
ALCHEMY_TELEMETRY_DISABLED: "1"
158+
steps:
159+
# The lock above is taken before this runs, so the state read here is
160+
# current. An open PR, including one reopened since the listing, is left
161+
# alone. GitHub reports merged PRs as MERGED, so anything not OPEN counts.
162+
- name: Confirm the PR is not open
163+
id: eligible
164+
run: |
165+
state=$(gh pr view "${{ matrix.number }}" --repo "$GITHUB_REPOSITORY" \
166+
--json state --jq 'if .state == "OPEN" then "no" else "yes" end') \
167+
|| { echo "Could not read PR ${{ matrix.number }}" >&2; exit 1; }
168+
echo "eligible=$state" >> "$GITHUB_OUTPUT"
169+
[ "$state" = yes ] || echo "PR ${{ matrix.number }} is open again; leaving $STAGE alone."
170+
171+
- name: Checkout default branch
172+
if: steps.eligible.outputs.eligible == 'yes'
173+
uses: actions/checkout@v6
174+
with:
175+
ref: ${{ github.event.repository.default_branch }}
176+
sparse-checkout: |
177+
/*
178+
!/.repos/
179+
sparse-checkout-cone-mode: false
180+
181+
- name: Setup Vite+
182+
if: steps.eligible.outputs.eligible == 'yes'
183+
uses: voidzero-dev/setup-vp@v1
184+
with:
185+
node-version-file: package.json
186+
cache: true
187+
run-install: |
188+
args:
189+
- --filter=t3code-relay...
190+
191+
- name: Destroy preview stage
192+
if: steps.eligible.outputs.eligible == 'yes'
193+
run: vp run --filter t3code-relay destroy --stage "$STAGE" --yes --no-input
194+
env:
195+
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
196+
PLANETSCALE_API_TOKEN_ID: ${{ secrets.PLANETSCALE_API_TOKEN_ID }}
197+
PLANETSCALE_API_TOKEN: ${{ secrets.PLANETSCALE_API_TOKEN }}
198+
AXIOM_TOKEN: ${{ secrets.AXIOM_TOKEN }}
199+
CLERK_SECRET_KEY: ${{ secrets.CLERK_SECRET_KEY }}
200+
201+
- name: Clear the teardown marker
202+
if: steps.eligible.outputs.eligible == 'yes'
203+
run: gh pr edit "${{ matrix.number }}" --repo "$GITHUB_REPOSITORY" --remove-label relay:preview

0 commit comments

Comments
 (0)