diff --git a/.github/scripts/ci-changes.cjs b/.github/scripts/ci-changes.cjs index 77a526076de4d..ed32670755ff6 100644 --- a/.github/scripts/ci-changes.cjs +++ b/.github/scripts/ci-changes.cjs @@ -14,7 +14,7 @@ const INERT = [ // Shared test setup and vendored mobile dependencies affect every package. const RUNS_EVERYTHING = [ - /^packages\/shared\/src\/(HostProcess|testing\/longTempDir)\.ts$/, + /^packages\/shared\/src\/(HostProcess|testing\/(longTempDir|providerEnv))\.ts$/, /^apps\/mobile\/deps\//, ]; @@ -27,6 +27,8 @@ const EXTRA_OWNERS = [ [/^native\/browser-secret\//, ["@t3tools/desktop"]], [/^native\/kde-snap-shot\//, ["@t3tools/desktop"]], [/^native\/libghostty-vt\//, ["@t3tools/web"]], + // apps/server/src/cloud/ConnectReachability.test.ts checks the guide's headings. + [/^docs\/user\/t3-connect-troubleshooting\.md$/, ["t3"]], ]; const RUST = /^native\/(resource-monitor|kde-snap-shot|hyprland-snap-shot)\//; @@ -92,11 +94,11 @@ function readWorkspace() { // [] means inert; null means unknown and requires every job. function ownersOf(file, packages) { - if (INERT.some((pattern) => pattern.test(file))) return []; + const extra = EXTRA_OWNERS.flatMap(([pattern, names]) => (pattern.test(file) ? names : [])); + if (INERT.some((pattern) => pattern.test(file))) return extra; // A manifest can change dependencies or versions other packages read. if (path.posix.basename(file) === "package.json") return null; if (RUNS_EVERYTHING.some((pattern) => pattern.test(file))) return null; - const extra = EXTRA_OWNERS.flatMap(([pattern, names]) => (pattern.test(file) ? names : [])); // scripts/build-desktop-artifact.test.ts reads the crates' sources. if (RUST.test(file)) return ["@t3tools/scripts", ...extra]; diff --git a/.github/scripts/t3-learn-ci.sh b/.github/scripts/t3-learn-ci.sh index daf41ad5f33ac..bd2c14fefc9eb 100755 --- a/.github/scripts/t3-learn-ci.sh +++ b/.github/scripts/t3-learn-ci.sh @@ -24,7 +24,8 @@ output_file="$root/.github/workflows/t3-learn-ci.yml" # Test Web runs on one 8 vCPU runner upstream. Browser test files share one # Chromium page, and on a slower 4 vCPU runner the heaviest file previews then # miss their 15s test and waitFor windows, so the fork splits Test Web across -# three runners. vp run passes a `--` through, so the flag follows the script. +# four runners. Upstream's PDF reload test still takes 13-15s there, so tests +# get 30s. vp run passes a `--` through, so the flags follow the script. transform=' s/^name: CI$/name: T3 Learn CI/ s/^( *)runs-on: blacksmith-[0-9]+vcpu-ubuntu-2404$/\1runs-on: ubuntu-24.04/ @@ -35,8 +36,8 @@ s/^( *)runs-on: blacksmith-[0-9]+vcpu-macos-26$/\1runs-on: macos-26/ strategy:\ fail-fast: false\ matrix:\ - shard: [1, 2, 3] - s/^( run: vp run --filter @t3tools\/web test)$/\1 --shard ${{ matrix.shard }}\/${{ strategy.job-total }}/ + shard: [1, 2, 3, 4] + s/^( run: vp run --filter @t3tools\/web test)$/\1 --shard ${{ matrix.shard }}\/${{ strategy.job-total }} --testTimeout=30000/ } /^ - name: Reject repository-owned PR assets$/i\ - name: Require t3-learn-ci.yml to match ci.yml\ diff --git a/.github/scripts/thread-transfer-report.cjs b/.github/scripts/thread-transfer-report.cjs index ca57769ed490c..09b25131e4c48 100644 --- a/.github/scripts/thread-transfer-report.cjs +++ b/.github/scripts/thread-transfer-report.cjs @@ -300,18 +300,28 @@ async function resolve({ github, context, core }) { core.setOutput("publish", "false"); return; } - const workflowRuns = await github.paginate(github.rest.actions.listWorkflowRuns, { - owner, - repo, - workflow_id: source.workflow_id, - branch: pull.base.ref, - event: "push", - status: "success", - per_page: 100, - }); + // One page each for the exact base and the newest runs; paging through + // every successful main run costs up to ten requests to keep 20. + const listRuns = async (filters) => { + const { data } = await github.rest.actions.listWorkflowRuns({ + owner, + repo, + workflow_id: source.workflow_id, + branch: pull.base.ref, + event: "push", + status: "success", + per_page: 20, + ...filters, + }); + return data.workflow_runs; + }; + const [baseRuns, recentRuns] = await Promise.all([ + listRuns({ head_sha: pull.base.sha }), + listRuns({}), + ]); const orderedRuns = [ - ...workflowRuns.filter((run) => run.head_sha === pull.base.sha), - ...workflowRuns.filter((run) => run.head_sha !== pull.base.sha), + ...baseRuns, + ...recentRuns.filter((run) => run.head_sha !== pull.base.sha), ].slice(0, 20); let baselineRun; diff --git a/.github/scripts/thread-transfer-report.test.cjs b/.github/scripts/thread-transfer-report.test.cjs index 753c3eb759072..8c4595ccf0c6a 100644 --- a/.github/scripts/thread-transfer-report.test.cjs +++ b/.github/scripts/thread-transfer-report.test.cjs @@ -55,7 +55,18 @@ test("validates the fixed artifact schema", () => { test("resolves a fallback PR with a redacted head repo and exact main baseline", async () => { const outputs = {}; const listWorkflowRunArtifacts = () => {}; - const listWorkflowRuns = () => {}; + // The exact base run sits far past the newest page of main CI runs. + const mainRuns = Array.from({ length: 1_000 }, (_, index) => ({ + id: 1_000 - index, + head_sha: index === 900 ? "base-sha" : `main-sha-${index}`, + })); + const listWorkflowRuns = async ({ head_sha, per_page }) => ({ + data: { + workflow_runs: mainRuns + .filter((run) => head_sha === undefined || run.head_sha === head_sha) + .slice(0, per_page), + }, + }); const listPullRequestsAssociatedWithCommit = () => {}; const github = { paginate: async (method, input) => { @@ -77,9 +88,6 @@ test("resolves a fallback PR with a redacted head repo and exact main baseline", }, ]; } - if (method === listWorkflowRuns) { - return [{ id: 1, head_sha: "base-sha" }]; - } throw new Error("unexpected pagination call"); }, rest: { @@ -123,7 +131,7 @@ test("resolves a fallback PR with a redacted head repo and exact main baseline", assert.equal(outputs.publish, "true"); assert.equal(outputs.pull_number, "5350"); assert.equal(outputs.pr_artifact, "true"); - assert.equal(outputs.baseline_run_id, "1"); + assert.equal(outputs.baseline_run_id, "100"); assert.equal(outputs.baseline_matches_base, "true"); }); diff --git a/.github/triage/PLAYBOOK.md b/.github/triage/PLAYBOOK.md index 78a0156d1eaa1..cc922de448005 100644 --- a/.github/triage/PLAYBOOK.md +++ b/.github/triage/PLAYBOOK.md @@ -125,10 +125,16 @@ of `main` for that work, never the tag-pinned diagnosis clone. - Show the user the complete final issue text and get an explicit yes before posting. Never post without it. - Note at the end of the issue which model and agent produced it. -- If `gh` is not authenticated, offer `gh auth login`, or build a prefilled - https://github.com/pingdotgg/t3code/issues/new URL with title and body query - parameters; print the URL, and open it in their browser only after they - approve. +- Post with `gh issue create --repo pingdotgg/t3code --label via-triage` only + if `gh api repos/pingdotgg/t3code --jq .permissions.triage` prints `true`. +- Otherwise, or if `gh` is not authenticated, build a prefilled + https://github.com/pingdotgg/t3code/issues/new?template=via-triage.yml URL. + The form applies the label whoever submits it. Add `title` plus one + URL-encoded query parameter per form field id: `what-happened`, `diagnosis`, + `steps`, `version`, `environment`, `evidence`, `related`, `workaround` and + `agent`. Keep the full encoded URL below 6,000 characters by shortening any + fields as needed. Give the user any omitted text to paste into the form before + submitting. Print the URL, and open it in their browser only after they approve. - If the user pasted screenshots, remind them to drag the images into the issue after it is created; they cannot be attached from here. diff --git a/.github/workflows/mobile-eas-preview-fork.yml b/.github/workflows/mobile-eas-preview-fork.yml new file mode 100644 index 0000000000000..e7615d46011bd --- /dev/null +++ b/.github/workflows/mobile-eas-preview-fork.yml @@ -0,0 +1,145 @@ +name: Mobile EAS Preview (Fork) + +# EAS previews for fork PRs. This runs the PR's code with an Expo token: the +# app config and plugins run on this runner, and native builds run on EAS +# with preview signing credentials. The label is the maintainer's sign-off +# that they reviewed that code, so it covers exactly the commit it was applied +# to: this pins that commit and consumes the label before running anything. A +# later push deploys nothing until the label is applied again. +# +# Same-repo PRs deploy on every push while labeled, via mobile-eas-preview.yml. + +on: + pull_request_target: + types: [labeled] + +permissions: + contents: read + +jobs: + preview: + name: EAS Preview + if: >- + github.event.pull_request.head.repo.full_name != github.repository && + github.event.label.name == '🚀 Mobile Continuous Deployment' + runs-on: blacksmith-8vcpu-ubuntu-2404 + concurrency: + group: mobile-eas-preview-fork-${{ github.event.pull_request.number }} + cancel-in-progress: true + permissions: + contents: read + pull-requests: write + env: + APP_VARIANT: preview + NODE_OPTIONS: --max-old-space-size=8192 + MOBILE_VERSION_POLICY: fingerprint + steps: + # If a maintainer removed the label first, that removal wins. + - id: consume + name: Consume the deployment label + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + try { + await github.rest.issues.removeLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + name: context.payload.label.name, + }); + core.setOutput("consumed", "true"); + } catch (error) { + if (error.status !== 404) throw error; + core.info("The label was removed before this run could consume it. Skipping."); + core.setOutput("consumed", "false"); + } + + - id: expo-token + name: Check for EXPO_PREVIEW_TOKEN + if: steps.consume.outputs.consumed == 'true' + env: + EXPO_TOKEN: ${{ secrets.EXPO_PREVIEW_TOKEN }} + run: | + if [ -n "$EXPO_TOKEN" ]; then + echo "present=true" >> "$GITHUB_OUTPUT" + else + echo "present=false" >> "$GITHUB_OUTPUT" + echo "EXPO_PREVIEW_TOKEN is not set; skipping EAS preview." + fi + + # The head SHA from the label event, not the branch, so a push after the + # label cannot swap in unreviewed code. No credentials stay in .git for + # the PR's scripts to read. + - name: Checkout reviewed commit + if: steps.expo-token.outputs.present == 'true' + uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + persist-credentials: false + # No sparse-checkout: see mobile-eas-preview.yml. + + # No caches: pull_request_target saves them under main's scope, where + # trusted workflows with other secrets would restore what PR code wrote. + - name: Setup Vite+ + if: steps.expo-token.outputs.present == 'true' + uses: voidzero-dev/setup-vp@v1 + with: + node-version-file: package.json + cache: false + run-install: | + args: + - --filter=@t3tools/mobile... + + - name: Expose pnpm + if: steps.expo-token.outputs.present == 'true' + run: | + pnpm_version="$(node --print "require('./package.json').packageManager.split('@').pop()")" + vp_pnpm_bin="$HOME/.vite-plus/package_manager/pnpm/$pnpm_version/pnpm/bin" + echo "$vp_pnpm_bin" >> "$GITHUB_PATH" + "$vp_pnpm_bin/pnpm" --version + + - name: Setup EAS + if: steps.expo-token.outputs.present == 'true' + uses: expo/expo-github-action@v8 + with: + # Pinned and installed with npm for the same reasons as mobile-eas-preview.yml. + eas-version: 21.8.0 + eas-cache: false + token: ${{ secrets.EXPO_PREVIEW_TOKEN }} + packager: npm + + - name: Pull preview environment variables + if: steps.expo-token.outputs.present == 'true' + working-directory: apps/mobile + env: + EXPO_TOKEN: ${{ secrets.EXPO_PREVIEW_TOKEN }} + run: | + eas env:pull preview --non-interactive + # See mobile-eas-preview.yml: keeps Android build and OTA fingerprints matching. + node --input-type=module <<'NODE' + import { appendFileSync, readFileSync } from "node:fs"; + import { parseEnv } from "node:util"; + const env = parseEnv(readFileSync(".env.local", "utf8")); + const googleServicesFile = env.T3CODE_ANDROID_GOOGLE_SERVICES_FILE; + if (googleServicesFile) { + appendFileSync(process.env.GITHUB_ENV, `T3CODE_ANDROID_GOOGLE_SERVICES_FILE=${googleServicesFile}\n`); + } + NODE + + - name: Deploy with fingerprint check + if: steps.expo-token.outputs.present == 'true' + uses: expo/expo-github-action/continuous-deploy-fingerprint@main + env: + EXPO_TOKEN: ${{ secrets.EXPO_PREVIEW_TOKEN }} + RUNNER_DEBUG: "1" + # The action only comments for pull_request events; this hook gives + # it the PR under pull_request_target. Without it, it skips the comment. + EXPO_TEST_GITHUB_PULL: ${{ github.event.pull_request.number }} + with: + profile: preview:dev + branch: pr-${{ github.event.pull_request.number }} + platform: all + environment: preview + working-directory: apps/mobile + github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/mobile-eas-preview.yml b/.github/workflows/mobile-eas-preview.yml index 107c741424b08..08183d0385811 100644 --- a/.github/workflows/mobile-eas-preview.yml +++ b/.github/workflows/mobile-eas-preview.yml @@ -7,7 +7,9 @@ on: jobs: preview: name: EAS Preview + # Fork PRs deploy one reviewed commit per label through mobile-eas-preview-fork.yml. if: | + github.event.pull_request.head.repo.full_name == github.repository && contains(github.event.pull_request.labels.*.name, '🚀 Mobile Continuous Deployment') && (github.event.action != 'labeled' || github.event.label.name == '🚀 Mobile Continuous Deployment') runs-on: blacksmith-8vcpu-ubuntu-2404 @@ -23,15 +25,15 @@ jobs: MOBILE_VERSION_POLICY: fingerprint steps: - id: expo-token - name: Check for EXPO_TOKEN + name: Check for EXPO_PREVIEW_TOKEN env: - EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} + EXPO_TOKEN: ${{ secrets.EXPO_PREVIEW_TOKEN }} run: | if [ -n "$EXPO_TOKEN" ]; then echo "present=true" >> "$GITHUB_OUTPUT" else echo "present=false" >> "$GITHUB_OUTPUT" - echo "EXPO_TOKEN is not available; skipping EAS preview." + echo "EXPO_PREVIEW_TOKEN is not available; skipping EAS preview." fi - name: Checkout @@ -70,7 +72,7 @@ jobs: # continuous-deploy-fingerprint still reads `build.project.ownerAccount`. # Unpin once https://github.com/expo/expo-github-action/issues/358 is fixed. eas-version: 21.8.0 - token: ${{ secrets.EXPO_TOKEN }} + token: ${{ secrets.EXPO_PREVIEW_TOKEN }} # npm, not pnpm: this only installs eas-cli into the action's own # tool dir, and pnpm 11 hard-fails that install on dtrace-provider's # ignored build script (no allowBuilds config outside the repo). @@ -80,7 +82,7 @@ jobs: if: steps.expo-token.outputs.present == 'true' working-directory: apps/mobile env: - EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} + EXPO_TOKEN: ${{ secrets.EXPO_PREVIEW_TOKEN }} run: | eas env:pull preview --non-interactive # EAS Update disables dotenv loading. Keep the downloaded native config @@ -99,7 +101,7 @@ jobs: if: steps.expo-token.outputs.present == 'true' uses: expo/expo-github-action/continuous-deploy-fingerprint@main env: - EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} + EXPO_TOKEN: ${{ secrets.EXPO_PREVIEW_TOKEN }} # The Expo action otherwise suppresses the EAS command's failure output. RUNNER_DEBUG: "1" with: diff --git a/.github/workflows/mobile-eas-production.yml b/.github/workflows/mobile-eas-production.yml index b2c03c04ab11c..6877f5c831a08 100644 --- a/.github/workflows/mobile-eas-production.yml +++ b/.github/workflows/mobile-eas-production.yml @@ -97,13 +97,13 @@ jobs: - id: expo-token name: Check for EXPO_TOKEN env: - EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} + EXPO_TOKEN: ${{ secrets.EXPO_PRODUCTION_TOKEN }} run: | if [ -n "$EXPO_TOKEN" ]; then echo "present=true" >> "$GITHUB_OUTPUT" else echo "present=false" >> "$GITHUB_OUTPUT" - echo "EXPO_TOKEN is not available; skipping EAS production job." + echo "EXPO_PRODUCTION_TOKEN is not available; skipping EAS production job." fi - id: version_app_token @@ -149,7 +149,7 @@ jobs: uses: expo/expo-github-action@v8 with: eas-version: latest - token: ${{ secrets.EXPO_TOKEN }} + token: ${{ secrets.EXPO_PRODUCTION_TOKEN }} # npm, not pnpm: this only installs eas-cli into the action's own # tool dir, and pnpm 11 hard-fails that install on dtrace-provider's # ignored build script (no allowBuilds config outside the repo). @@ -159,7 +159,7 @@ jobs: if: steps.expo-token.outputs.present == 'true' working-directory: apps/mobile env: - EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} + EXPO_TOKEN: ${{ secrets.EXPO_PRODUCTION_TOKEN }} run: | eas env:pull production --non-interactive # EAS commands skip dotenv, and the Android fingerprint hashes this @@ -215,7 +215,7 @@ jobs: git config user.name "${APP_SLUG}[bot]" git config user.email "${user_id}+${APP_SLUG}[bot]@users.noreply.github.com" git add apps/mobile/app.config.ts - git commit \ + git commit --no-verify \ -m "chore(mobile): bump app version to $RELEASE_VERSION" \ -m "Co-authored-by: codex " git push origin "HEAD:refs/heads/${GITHUB_REF_NAME}" @@ -244,7 +244,7 @@ jobs: if: steps.expo-token.outputs.present == 'true' && github.event_name == 'workflow_dispatch' && inputs.mode == 'build' working-directory: apps/mobile env: - EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} + EXPO_TOKEN: ${{ secrets.EXPO_PRODUCTION_TOKEN }} MOBILE_BUILD_PROFILE: ${{ inputs.profile || 'production' }} run: eas build --platform ${{ inputs.platform }} --profile "$MOBILE_BUILD_PROFILE" --auto-submit --non-interactive --no-wait @@ -255,7 +255,7 @@ jobs: if: steps.expo-token.outputs.present == 'true' && (github.event_name == 'push' || inputs.mode == 'update') working-directory: apps/mobile env: - EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} + EXPO_TOKEN: ${{ secrets.EXPO_PRODUCTION_TOKEN }} PLATFORM: ${{ inputs.platform || 'all' }} run: | if [ "$PLATFORM" = "all" ]; then platforms="ios android"; else platforms="$PLATFORM"; fi @@ -275,7 +275,7 @@ jobs: if: steps.expo-token.outputs.present == 'true' && github.event_name == 'workflow_dispatch' && inputs.mode == 'update' working-directory: apps/mobile env: - EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} + EXPO_TOKEN: ${{ secrets.EXPO_PRODUCTION_TOKEN }} MOBILE_BUILD_PROFILE: ${{ inputs.profile || 'production' }} OTA_MESSAGE: ${{ inputs.message || format('Production OTA ({0})', github.sha) }} run: | @@ -299,7 +299,7 @@ jobs: if: steps.expo-token.outputs.present == 'true' && github.event_name == 'push' working-directory: apps/mobile env: - EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} + EXPO_TOKEN: ${{ secrets.EXPO_PRODUCTION_TOKEN }} run: | failed=0 message="$(git log -1 --pretty=%s | head -c 120) ($(git rev-parse --short=9 HEAD))" diff --git a/.github/workflows/pr-vouch.yml b/.github/workflows/pr-vouch.yml index c4abb08b727b2..75e2ee8a56548 100644 --- a/.github/workflows/pr-vouch.yml +++ b/.github/workflows/pr-vouch.yml @@ -20,6 +20,10 @@ permissions: jobs: collect-targets: name: Collect PR targets + # Most comments are not recheck commands; skip them before a runner starts. + if: >- + github.event_name != 'issue_comment' || + (github.event.issue.pull_request && contains(github.event.comment.body, '/recheck-vouch')) runs-on: ubuntu-24.04 outputs: targets: ${{ steps.collect.outputs.targets }} diff --git a/.github/workflows/release-desktop.yml b/.github/workflows/release-desktop.yml index 2c09b96227bd8..8601289ee37d5 100644 --- a/.github/workflows/release-desktop.yml +++ b/.github/workflows/release-desktop.yml @@ -203,6 +203,14 @@ jobs: native/hyprland-snap-shot/target/${{ inputs.rust_target }}/release/t3-hyprland-snap-shot key: linux-capture-helpers-${{ inputs.rust_target }}-${{ hashFiles('native/kde-snap-shot/Cargo.lock', 'native/kde-snap-shot/Cargo.toml', 'native/kde-snap-shot/src/**', 'native/hyprland-snap-shot/Cargo.lock', 'native/hyprland-snap-shot/Cargo.toml', 'native/hyprland-snap-shot/src/**', 'native/hyprland-snap-shot/protocols/**') }} + - name: Ensure Rosetta for macOS x64 bytecode generation + if: inputs.platform == 'mac' && inputs.arch == 'x64' + shell: bash + run: | + if [[ "$(uname -m)" == "arm64" ]] && ! /usr/bin/arch -x86_64 /usr/bin/true; then + sudo /usr/sbin/softwareupdate --install-rosetta --agree-to-license + fi + - name: Setup Rust if: steps.resource_monitor_cache.outputs.cache-hit != 'true' || (inputs.platform == 'linux' && steps.capture_helper_cache.outputs.cache-hit != 'true') uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6d89b7c7abe7a..82f39f8034cc4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -395,8 +395,12 @@ jobs: (github.event_name != 'schedule' || needs.resolve_commit.outputs.has_changes == 'true') runs-on: blacksmith-8vcpu-ubuntu-2404 timeout-minutes: 5 + # Previews build any branch, but production admits only main and v* tags. + # The empty name runs previews with no environment, so their Clerk and relay + # ids come from .env.example. Everything else matches nightly: the tracing + # read below needs only repository-level Cloudflare credentials. environment: - name: production + name: ${{ inputs.channel != 'preview' && 'production' || '' }} outputs: clerk_publishable_key: ${{ steps.public_config.outputs.clerk_publishable_key }} clerk_jwt_template: ${{ steps.public_config.outputs.clerk_jwt_template }} @@ -462,9 +466,19 @@ jobs: - id: public_config name: Resolve production relay public config shell: bash + env: + PREVIEW: ${{ inputs.channel == 'preview' }} run: | set -euo pipefail + if [[ "$PREVIEW" == "true" ]]; then + env_example() { sed -n "s|^T3CODE_$1=||p" .env.example; } + CLERK_PUBLISHABLE_KEY="$(env_example CLERK_PUBLISHABLE_KEY)" + CLERK_JWT_TEMPLATE="$(env_example CLERK_JWT_TEMPLATE)" + CLERK_CLI_OAUTH_CLIENT_ID="$(env_example CLERK_CLI_OAUTH_CLIENT_ID)" + RELAY_DOMAIN="$(sed -n 's|^T3CODE_RELAY_URL=https://||p' .env.example)" + fi + relay_domain="${RELAY_DOMAIN:-}" if [[ -z "$relay_domain" && -n "${RELAY_API_ZONE_NAME:-}" ]]; then relay_domain="relay.$RELAY_API_ZONE_NAME" @@ -1388,7 +1402,7 @@ jobs: git config user.email "${{ steps.app_bot.outputs.email }}" git add apps/server/package.json apps/desktop/package.json apps/web/package.json packages/contracts/package.json pnpm-lock.yaml - git commit -m "chore(release): prepare $RELEASE_TAG" + git commit --no-verify -m "chore(release): prepare $RELEASE_TAG" git push origin HEAD:main announce_discord: diff --git a/.github/workflows/t3-learn-ci.yml b/.github/workflows/t3-learn-ci.yml index 95c6f7342ba8b..7b341b8dab336 100644 --- a/.github/workflows/t3-learn-ci.yml +++ b/.github/workflows/t3-learn-ci.yml @@ -300,7 +300,7 @@ jobs: strategy: fail-fast: false matrix: - shard: [1, 2, 3] + shard: [1, 2, 3, 4] timeout-minutes: 10 steps: - name: Checkout @@ -343,7 +343,7 @@ jobs: fi - name: Test - run: vp run --filter @t3tools/web test --shard ${{ matrix.shard }}/${{ strategy.job-total }} + run: vp run --filter @t3tools/web test --shard ${{ matrix.shard }}/${{ strategy.job-total }} --testTimeout=30000 # Browser tests run source modules; this boots the production chunk graph. - name: Boot the built web client diff --git a/.macroscope/check-run-agents/ui-consistency.md b/.macroscope/check-run-agents/ui-consistency.md index 047dafbdd4be1..905823d1d1a9e 100644 --- a/.macroscope/check-run-agents/ui-consistency.md +++ b/.macroscope/check-run-agents/ui-consistency.md @@ -44,7 +44,7 @@ The reference for how this codebase wants UI built is the composer banner system - `ComposerBanner.tsx` exports one object of small slot components (`Root`, `Row`, `Icon`, `Content`, `Actions`, `Dismiss`, `Children`, and so on). Each slot owns its own Tailwind classes, exposes a `data-slot` attribute, takes `className` and spreads the rest of its props, and uses `cn` so callers can adjust without overriding. Variants and density are props on `Root`, not ad hoc class strings at call sites. Where a slot needs a real control it renders `Button` from `ui/` rather than a styled `