Release #752
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| on: | |
| push: | |
| tags: | |
| - "v*.*.*" | |
| - "!v*-nightly.*" | |
| schedule: | |
| - cron: "0 */3 * * *" | |
| workflow_dispatch: | |
| inputs: | |
| channel: | |
| description: "Release channel" | |
| required: false | |
| default: stable | |
| type: choice | |
| options: | |
| - stable | |
| - nightly | |
| version: | |
| description: "Release version (for example 1.2.3 or v1.2.3)" | |
| required: false | |
| type: string | |
| permissions: | |
| contents: write | |
| id-token: none | |
| env: | |
| T3CODE_RELEASE_REPOSITORY: aaditagrawal/t3code | |
| T3CODE_DESKTOP_UPDATE_REPOSITORY: aaditagrawal/t3code | |
| jobs: | |
| check_changes: | |
| name: Check for changes since last nightly | |
| if: github.event_name == 'schedule' | |
| runs-on: ubuntu-24.04 # blacksmith-8vcpu-ubuntu-2404 | |
| outputs: | |
| has_changes: ${{ steps.check.outputs.has_changes }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| sparse-checkout: | | |
| /* | |
| !/.repos/ | |
| sparse-checkout-cone-mode: false | |
| - id: check | |
| name: Compare HEAD to last nightly tag | |
| run: | | |
| last_nightly_tag=$(git tag --list 'v*-nightly.*' 'nightly-v*' --sort=-creatordate | head -n 1) | |
| if [[ -z "$last_nightly_tag" ]]; then | |
| echo "No previous nightly tag found. Proceeding with release." | |
| echo "has_changes=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| last_nightly_sha=$(git rev-parse "$last_nightly_tag^{commit}") | |
| head_sha=$(git rev-parse HEAD) | |
| if [[ "$last_nightly_sha" == "$head_sha" ]]; then | |
| echo "No changes on main since last nightly release ($last_nightly_tag). Skipping." | |
| echo "has_changes=false" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "Changes detected on main since $last_nightly_tag ($last_nightly_sha → $head_sha). Proceeding." | |
| echo "has_changes=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| preflight: | |
| name: Preflight | |
| needs: [check_changes] | |
| if: | | |
| !failure() && !cancelled() && | |
| (github.event_name != 'schedule' || needs.check_changes.outputs.has_changes == 'true') | |
| runs-on: ubuntu-24.04 # blacksmith-8vcpu-ubuntu-2404 | |
| # Sized for a GitHub-hosted runner: check, typecheck and the full test | |
| # suite run here in sequence. | |
| timeout-minutes: 30 | |
| outputs: | |
| release_channel: ${{ steps.release_meta.outputs.release_channel }} | |
| version: ${{ steps.release_meta.outputs.version }} | |
| tag: ${{ steps.release_meta.outputs.tag }} | |
| release_name: ${{ steps.release_meta.outputs.name }} | |
| short_sha: ${{ steps.release_meta.outputs.short_sha }} | |
| previous_tag: ${{ steps.previous_tag.outputs.previous_tag }} | |
| cli_dist_tag: ${{ steps.release_meta.outputs.cli_dist_tag }} | |
| is_prerelease: ${{ steps.release_meta.outputs.is_prerelease }} | |
| make_latest: ${{ steps.release_meta.outputs.make_latest }} | |
| release_repository: ${{ steps.release_meta.outputs.release_repository }} | |
| ref: ${{ github.sha }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| sparse-checkout: | | |
| /* | |
| !/.repos/ | |
| sparse-checkout-cone-mode: false | |
| - name: Setup Vite+ | |
| uses: voidzero-dev/setup-vp@v1 | |
| with: | |
| node-version-file: package.json | |
| cache: true | |
| run-install: true | |
| - name: Ensure Electron runtime is installed | |
| run: vp run --filter @t3tools/desktop ensure:electron | |
| - id: release_meta | |
| name: Resolve release version | |
| shell: bash | |
| env: | |
| DISPATCH_CHANNEL: ${{ github.event.inputs.channel }} | |
| DISPATCH_VERSION: ${{ github.event.inputs.version }} | |
| NIGHTLY_DATE: ${{ github.run_started_at }} | |
| NIGHTLY_SHA: ${{ github.sha }} | |
| NIGHTLY_RUN_NUMBER: ${{ github.run_number }} | |
| run: | | |
| release_repository="${T3CODE_RELEASE_REPOSITORY:?T3CODE_RELEASE_REPOSITORY is required}" | |
| desktop_update_repository="${T3CODE_DESKTOP_UPDATE_REPOSITORY:?T3CODE_DESKTOP_UPDATE_REPOSITORY is required}" | |
| for repository in "$release_repository" "$desktop_update_repository"; do | |
| if [[ ! "$repository" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]]; then | |
| echo "Invalid release repository: $repository" >&2 | |
| exit 1 | |
| fi | |
| done | |
| if [[ "$GITHUB_REPOSITORY" != "$release_repository" || "$GITHUB_REPOSITORY" != "$desktop_update_repository" ]]; then | |
| echo "This fork release workflow is configured for release repository $release_repository and desktop updater repository $desktop_update_repository, but it is running in $GITHUB_REPOSITORY." >&2 | |
| exit 1 | |
| fi | |
| echo "release_repository=$release_repository" >> "$GITHUB_OUTPUT" | |
| if [[ "${GITHUB_EVENT_NAME}" == "schedule" || ( "${GITHUB_EVENT_NAME}" == "workflow_dispatch" && "${DISPATCH_CHANNEL:-stable}" == "nightly" ) ]]; then | |
| nightly_date="$(date -u -d "$NIGHTLY_DATE" +%Y%m%d)" | |
| node scripts/resolve-nightly-release.ts \ | |
| --date "$nightly_date" \ | |
| --run-number "$NIGHTLY_RUN_NUMBER" \ | |
| --sha "$NIGHTLY_SHA" \ | |
| --github-output | |
| echo "release_channel=nightly" >> "$GITHUB_OUTPUT" | |
| echo "cli_dist_tag=nightly" >> "$GITHUB_OUTPUT" | |
| echo "is_prerelease=true" >> "$GITHUB_OUTPUT" | |
| echo "make_latest=false" >> "$GITHUB_OUTPUT" | |
| else | |
| if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then | |
| raw="${DISPATCH_VERSION}" | |
| if [[ -z "$raw" ]]; then | |
| echo "workflow_dispatch stable releases require the version input." >&2 | |
| exit 1 | |
| fi | |
| else | |
| raw="${GITHUB_REF_NAME}" | |
| fi | |
| version="${raw#v}" | |
| if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then | |
| echo "Invalid release version: $raw" >&2 | |
| exit 1 | |
| fi | |
| echo "release_channel=stable" >> "$GITHUB_OUTPUT" | |
| echo "version=$version" >> "$GITHUB_OUTPUT" | |
| echo "tag=v$version" >> "$GITHUB_OUTPUT" | |
| echo "name=T3 Code v$version" >> "$GITHUB_OUTPUT" | |
| echo "cli_dist_tag=latest" >> "$GITHUB_OUTPUT" | |
| if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| echo "is_prerelease=false" >> "$GITHUB_OUTPUT" | |
| echo "make_latest=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "is_prerelease=true" >> "$GITHUB_OUTPUT" | |
| echo "make_latest=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| fi | |
| - name: Check | |
| run: vp check | |
| - name: Typecheck | |
| run: vp run typecheck | |
| - name: Test | |
| run: vp run test | |
| - id: previous_tag | |
| name: Resolve previous release tag | |
| run: | | |
| node scripts/resolve-previous-release-tag.ts \ | |
| --channel "${{ steps.release_meta.outputs.release_channel }}" \ | |
| --current-tag "${{ steps.release_meta.outputs.tag }}" \ | |
| --github-output | |
| relay_public_config: | |
| name: Resolve T3 Connect public config | |
| needs: preflight | |
| if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' }} | |
| runs-on: ubuntu-24.04 # blacksmith-8vcpu-ubuntu-2404 | |
| timeout-minutes: 5 | |
| environment: | |
| name: production | |
| outputs: | |
| clerk_publishable_key: ${{ steps.public_config.outputs.clerk_publishable_key }} | |
| clerk_jwt_template: ${{ steps.public_config.outputs.clerk_jwt_template }} | |
| clerk_cli_oauth_client_id: ${{ steps.public_config.outputs.clerk_cli_oauth_client_id }} | |
| relay_url: ${{ steps.public_config.outputs.relay_url }} | |
| env: | |
| CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }} | |
| CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| RELAY_DOMAIN: ${{ vars.RELAY_DOMAIN }} | |
| RELAY_API_ZONE_NAME: ${{ vars.RELAY_API_ZONE_NAME }} | |
| CLERK_PUBLISHABLE_KEY: ${{ vars.CLERK_PUBLISHABLE_KEY }} | |
| CLERK_JWT_TEMPLATE: ${{ vars.CLERK_JWT_TEMPLATE }} | |
| CLERK_CLI_OAUTH_CLIENT_ID: ${{ vars.CLERK_CLI_OAUTH_CLIENT_ID }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.preflight.outputs.ref }} | |
| sparse-checkout: | | |
| /* | |
| !/.repos/ | |
| sparse-checkout-cone-mode: false | |
| - name: Setup Vite+ | |
| uses: voidzero-dev/setup-vp@v1 | |
| with: | |
| node-version-file: package.json | |
| cache: true | |
| run-install: | | |
| args: | |
| - --filter=t3code-relay... | |
| - id: relay_state | |
| name: Read production relay tracing config | |
| continue-on-error: true | |
| shell: bash | |
| run: | | |
| vp run --filter t3code-relay deploy \ | |
| --stage prod \ | |
| --read-state \ | |
| --github-output \ | |
| --github-env-file "$RUNNER_TEMP/relay-client-tracing.env" | |
| - name: Upload relay client tracing config | |
| continue-on-error: true | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: relay-client-tracing-config | |
| path: ${{ runner.temp }}/relay-client-tracing.env | |
| if-no-files-found: warn | |
| retention-days: 1 | |
| - id: public_config | |
| name: Resolve production relay public config | |
| if: always() | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| relay_domain="${RELAY_DOMAIN:-}" | |
| if [[ -z "$relay_domain" && -n "${RELAY_API_ZONE_NAME:-}" ]]; then | |
| relay_domain="relay.$RELAY_API_ZONE_NAME" | |
| fi | |
| required=( | |
| relay_domain | |
| CLERK_PUBLISHABLE_KEY | |
| CLERK_JWT_TEMPLATE | |
| CLERK_CLI_OAUTH_CLIENT_ID | |
| ) | |
| missing=() | |
| for name in "${required[@]}"; do | |
| if [[ -z "${!name:-}" ]]; then | |
| missing+=("$name") | |
| fi | |
| done | |
| if (( ${#missing[@]} > 0 )); then | |
| # Fork: relay/Clerk production config is optional for desktop releases. | |
| printf 'Relay public config unavailable (%s); continuing without hosted relay wiring.\n' "${missing[*]}" >&2 | |
| echo "clerk_publishable_key=" >> "$GITHUB_OUTPUT" | |
| echo "clerk_jwt_template=" >> "$GITHUB_OUTPUT" | |
| echo "clerk_cli_oauth_client_id=" >> "$GITHUB_OUTPUT" | |
| echo "relay_url=" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| echo "clerk_publishable_key=$CLERK_PUBLISHABLE_KEY" >> "$GITHUB_OUTPUT" | |
| echo "clerk_jwt_template=$CLERK_JWT_TEMPLATE" >> "$GITHUB_OUTPUT" | |
| echo "clerk_cli_oauth_client_id=$CLERK_CLI_OAUTH_CLIENT_ID" >> "$GITHUB_OUTPUT" | |
| echo "relay_url=https://$relay_domain" >> "$GITHUB_OUTPUT" | |
| # node-pty publishes no Linux prebuilt and the WSL backend runs under the | |
| # distro's own (Linux) Node, which can't load the Windows/Electron binary. We | |
| # build the Linux pty.node here, on Linux, and hand it to the Windows packaging | |
| # job — the Windows artifact then ships a ready WSL backend binary with no | |
| # cross-compiling and no first-launch compiler/node-gyp/network on the user's | |
| # machine. node-pty is N-API, so one binary works across all WSL Node versions. | |
| build_wsl_node_pty: | |
| name: Build WSL node-pty (linux-x64) | |
| needs: [preflight] | |
| if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' }} | |
| # Build against Debian bullseye glibc (≈ Ubuntu 20.04) so the bundled WSL | |
| # native addon loads on common Ubuntu 20.04/22.04 WSL distros. N-API covers | |
| # Node ABI, not libc ABI. Use a standard GitHub-hosted runner + Node bullseye | |
| # container instead of Blacksmith Ubuntu 2004 labels, which can queue forever. | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.preflight.outputs.ref }} | |
| sparse-checkout: | | |
| /* | |
| !/.repos/ | |
| sparse-checkout-cone-mode: false | |
| - name: Setup Vite+ | |
| uses: voidzero-dev/setup-vp@v1 | |
| with: | |
| node-version-file: package.json | |
| cache: true | |
| run-install: | | |
| args: | |
| - --filter=t3... | |
| - name: Build node-pty linux-x64 prebuild | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| # Resolve node-pty from apps/server (where it's a dependency) and build | |
| # its native binary from source for Linux against an older glibc. node- | |
| # addon-api resolves from node-pty's own dependency tree inside the | |
| # mounted workspace, so node-gyp has everything it needs. | |
| pty_pkg="$(node -e "console.log(require.resolve('node-pty/package.json', { paths: ['$GITHUB_WORKSPACE/apps/server'] }))")" | |
| pty_dir="$(dirname "$pty_pkg")" | |
| docker run --rm \ | |
| -v "$GITHUB_WORKSPACE:$GITHUB_WORKSPACE" \ | |
| -w "$pty_dir" \ | |
| node:24.13.1-bullseye \ | |
| bash -lc 'apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq build-essential python3 >/dev/null && npx --yes node-gyp rebuild' | |
| mkdir -p wsl-prebuild | |
| cp "$pty_dir/build/Release/pty.node" wsl-prebuild/pty.node | |
| file wsl-prebuild/pty.node | |
| - name: Upload node-pty linux-x64 prebuild | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: wsl-node-pty-x64 | |
| path: wsl-prebuild/pty.node | |
| if-no-files-found: error | |
| build: | |
| name: Build ${{ matrix.label }} | |
| # build_wsl_node_pty stays in `needs` so it runs first and its artifact is | |
| # available to download, but only the Windows matrix entry consumes it. We | |
| # Gate the job on preflight only (fork: no relay requirement). `!cancelled()` | |
| # (not `!failure()`) lets macOS/Linux builds run even when build_wsl_node_pty | |
| # failed; the Windows-only download step below then fails that single | |
| # platform if the prebuild is missing. | |
| needs: [preflight, build_wsl_node_pty] | |
| # Fork: desktop builds must not depend on upstream relay/Clerk production config. | |
| if: ${{ !cancelled() && needs.preflight.result == 'success' }} | |
| runs-on: ${{ matrix.runner }} | |
| # Packaging plus the Rust resource-monitor build, moved from 12-32 vCPU | |
| # runners onto standard GitHub-hosted ones. | |
| timeout-minutes: 60 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - label: macOS arm64 | |
| runner: macos-26 # blacksmith-12vcpu-macos-26 | |
| platform: mac | |
| target: dmg | |
| arch: arm64 | |
| rust_target: aarch64-apple-darwin | |
| resource_key: darwin-arm64 | |
| - label: macOS x64 | |
| runner: macos-26 # blacksmith-12vcpu-macos-26 | |
| platform: mac | |
| target: dmg | |
| arch: x64 | |
| rust_target: x86_64-apple-darwin | |
| resource_key: darwin-x64 | |
| - label: Linux x64 | |
| runner: ubuntu-24.04 # blacksmith-32vcpu-ubuntu-2404 | |
| platform: linux | |
| target: AppImage | |
| arch: x64 | |
| rust_target: x86_64-unknown-linux-gnu | |
| resource_key: linux-x64 | |
| - label: Windows x64 | |
| runner: windows-2025 # blacksmith-32vcpu-windows-2025 | |
| platform: win | |
| target: nsis | |
| arch: x64 | |
| rust_target: x86_64-pc-windows-msvc | |
| resource_key: win32-x64 | |
| # - label: Windows arm64 | |
| # runner: windows-11-arm | |
| # platform: win | |
| # target: nsis | |
| # arch: arm64 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.preflight.outputs.ref }} | |
| sparse-checkout: | | |
| /* | |
| !/.repos/ | |
| sparse-checkout-cone-mode: false | |
| - name: Setup Vite+ | |
| uses: voidzero-dev/setup-vp@v1 | |
| with: | |
| node-version-file: package.json | |
| cache: true | |
| run-install: | | |
| args: | |
| - --filter=@t3tools/desktop... | |
| - --filter=t3... | |
| - --filter=@t3tools/scripts... | |
| - name: Setup Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.rust_target }} | |
| - name: Download relay client tracing config | |
| continue-on-error: true | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: relay-client-tracing-config | |
| path: ${{ runner.temp }}/relay-client-tracing | |
| - name: Load relay client tracing config | |
| shell: bash | |
| run: | | |
| config_path="$RUNNER_TEMP/relay-client-tracing/relay-client-tracing.env" | |
| if [[ ! -f "$config_path" ]]; then | |
| echo "No relay client tracing config artifact; continuing without it." | |
| exit 0 | |
| fi | |
| tracing_token="$(sed -n 's/^T3CODE_RELAY_CLIENT_OTLP_TRACES_TOKEN=//p' "$config_path")" | |
| echo "::add-mask::$tracing_token" | |
| cat "$config_path" >> "$GITHUB_ENV" | |
| - name: Align package versions to release version | |
| run: node scripts/update-release-package-versions.ts "${{ needs.preflight.outputs.version }}" | |
| - name: Download WSL node-pty prebuild | |
| if: matrix.platform == 'win' | |
| uses: actions/download-artifact@v7 | |
| with: | |
| name: wsl-node-pty-x64 | |
| path: wsl-prebuild | |
| - name: Install Spectre-mitigated MSVC libs | |
| if: matrix.platform == 'win' | |
| shell: pwsh | |
| run: | | |
| $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" | |
| $installPath = & $vswhere -products * -latest -property installationPath | |
| $setupExe = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\setup.exe" | |
| $proc = Start-Process -FilePath $setupExe ` | |
| -ArgumentList "modify", "--installPath", "`"$installPath`"", "--add", ` | |
| "Microsoft.VisualStudio.Component.VC.Tools.x86.x64.Spectre", "--quiet", "--norestart" ` | |
| -Wait -PassThru -NoNewWindow | |
| if ($null -eq $proc -or $proc.ExitCode -ne 0) { | |
| $code = if ($null -ne $proc) { $proc.ExitCode } else { 1 } | |
| Write-Error "Visual Studio Installer failed with exit code $code" | |
| exit $code | |
| } | |
| - name: Install ImageMagick | |
| if: matrix.platform == 'linux' | |
| shell: bash | |
| run: | | |
| if ! command -v magick >/dev/null 2>&1 && ! command -v convert >/dev/null 2>&1; then | |
| sudo apt-get update | |
| sudo apt-get install -y imagemagick | |
| fi | |
| if command -v magick >/dev/null 2>&1; then | |
| magick -version | |
| else | |
| convert -version | |
| fi | |
| - name: Prepare Azure Trusted Signing | |
| if: matrix.platform == 'win' | |
| shell: pwsh | |
| env: | |
| AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} | |
| AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} | |
| AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} | |
| AZURE_TRUSTED_SIGNING_ENDPOINT: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }} | |
| AZURE_TRUSTED_SIGNING_ACCOUNT_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }} | |
| AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME }} | |
| AZURE_TRUSTED_SIGNING_PUBLISHER_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_PUBLISHER_NAME }} | |
| run: | | |
| $ErrorActionPreference = "Stop" | |
| $requiredSecrets = @( | |
| $env:AZURE_TENANT_ID, | |
| $env:AZURE_CLIENT_ID, | |
| $env:AZURE_CLIENT_SECRET, | |
| $env:AZURE_TRUSTED_SIGNING_ENDPOINT, | |
| $env:AZURE_TRUSTED_SIGNING_ACCOUNT_NAME, | |
| $env:AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME, | |
| $env:AZURE_TRUSTED_SIGNING_PUBLISHER_NAME | |
| ) | |
| if ($requiredSecrets | Where-Object { [string]::IsNullOrWhiteSpace($_) }) { | |
| Write-Host "Azure Trusted Signing disabled; skipping TrustedSigning module preparation." | |
| exit 0 | |
| } | |
| try { | |
| Install-PackageProvider ` | |
| -Name NuGet ` | |
| -MinimumVersion 2.8.5.201 ` | |
| -Force ` | |
| -Scope CurrentUser ` | |
| -ErrorAction Stop | |
| } catch { | |
| Write-Warning "Could not bootstrap NuGet package provider. Continuing because the runner may already have a usable provider. $($_.Exception.Message)" | |
| } | |
| Install-Module ` | |
| -Name TrustedSigning ` | |
| -MinimumVersion 0.5.0 ` | |
| -Force ` | |
| -AllowClobber ` | |
| -Repository PSGallery ` | |
| -Scope CurrentUser ` | |
| -ErrorAction Stop | |
| Import-Module TrustedSigning -MinimumVersion 0.5.0 -Force | |
| Get-Command Invoke-TrustedSigning -ErrorAction Stop | |
| $moduleRoots = @( | |
| [System.IO.Path]::Combine([Environment]::GetFolderPath("MyDocuments"), "PowerShell", "Modules"), | |
| [System.IO.Path]::Combine([Environment]::GetFolderPath("MyDocuments"), "WindowsPowerShell", "Modules"), | |
| [System.IO.Path]::Combine($env:ProgramFiles, "PowerShell", "Modules"), | |
| [System.IO.Path]::Combine($env:ProgramFiles, "WindowsPowerShell", "Modules") | |
| ) | |
| $modulePathEntries = @($moduleRoots + ($env:PSModulePath -split ";")) | | |
| Where-Object { $_ -and (Test-Path $_) } | | |
| Select-Object -Unique | |
| "PSModulePath=$($modulePathEntries -join ';')" >> $env:GITHUB_ENV | |
| - name: Build desktop artifact | |
| shell: bash | |
| env: | |
| CSC_LINK: ${{ secrets.CSC_LINK }} | |
| CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} | |
| APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} | |
| APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} | |
| APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} | |
| APPLE_TEAM_ID: ${{ vars.APPLE_TEAM_ID }} | |
| MACOS_PROVISIONING_PROFILE: ${{ secrets.MACOS_PROVISIONING_PROFILE }} | |
| T3CODE_CLERK_PASSKEY_RP_DOMAINS: ${{ vars.CLERK_PASSKEY_RP_DOMAINS }} | |
| AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} | |
| AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} | |
| AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} | |
| AZURE_TRUSTED_SIGNING_ENDPOINT: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }} | |
| AZURE_TRUSTED_SIGNING_ACCOUNT_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }} | |
| AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME }} | |
| AZURE_TRUSTED_SIGNING_PUBLISHER_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_PUBLISHER_NAME }} | |
| run: | | |
| args=( | |
| --platform "${{ matrix.platform }}" | |
| --target "${{ matrix.target }}" | |
| --arch "${{ matrix.arch }}" | |
| --build-version "${{ needs.preflight.outputs.version }}" | |
| --verbose | |
| ) | |
| has_all() { | |
| for value in "$@"; do | |
| if [[ -z "$value" ]]; then | |
| return 1 | |
| fi | |
| done | |
| return 0 | |
| } | |
| if [[ "${{ matrix.platform }}" == "mac" ]]; then | |
| if has_all "$CSC_LINK" "$CSC_KEY_PASSWORD" "$APPLE_API_KEY" "$APPLE_API_KEY_ID" "$APPLE_API_ISSUER"; then | |
| if ! has_all "$APPLE_TEAM_ID" "$MACOS_PROVISIONING_PROFILE"; then | |
| echo "macOS signing is configured, but APPLE_TEAM_ID or MACOS_PROVISIONING_PROFILE is missing." >&2 | |
| exit 1 | |
| fi | |
| key_path="$RUNNER_TEMP/AuthKey_${APPLE_API_KEY_ID}.p8" | |
| printf '%s' "$APPLE_API_KEY" > "$key_path" | |
| export APPLE_API_KEY="$key_path" | |
| profile_path="$RUNNER_TEMP/t3code.provisionprofile" | |
| printf '%s' "$MACOS_PROVISIONING_PROFILE" | base64 -D > "$profile_path" | |
| security cms -D -i "$profile_path" >/dev/null | |
| export T3CODE_APPLE_TEAM_ID="$APPLE_TEAM_ID" | |
| export T3CODE_MACOS_PROVISIONING_PROFILE="$profile_path" | |
| echo "macOS signing enabled." | |
| args+=(--signed) | |
| else | |
| echo "macOS signing disabled (missing one or more Apple signing secrets)." | |
| fi | |
| elif [[ "${{ matrix.platform }}" == "win" ]]; then | |
| # Bundle the Linux node-pty binary built by the build_wsl_node_pty job | |
| # so the packaged WSL backend ships a ready binary (no first-launch | |
| # compile). Required for a working WSL backend on Windows. | |
| args+=(--wsl-prebuild "$GITHUB_WORKSPACE/wsl-prebuild/pty.node") | |
| if has_all \ | |
| "$AZURE_TENANT_ID" \ | |
| "$AZURE_CLIENT_ID" \ | |
| "$AZURE_CLIENT_SECRET" \ | |
| "$AZURE_TRUSTED_SIGNING_ENDPOINT" \ | |
| "$AZURE_TRUSTED_SIGNING_ACCOUNT_NAME" \ | |
| "$AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME" \ | |
| "$AZURE_TRUSTED_SIGNING_PUBLISHER_NAME"; then | |
| echo "Windows signing enabled (Azure Trusted Signing)." | |
| args+=(--signed) | |
| else | |
| echo "Windows signing disabled (missing one or more Azure Trusted Signing secrets)." | |
| fi | |
| else | |
| echo "Signing disabled for ${{ matrix.platform }}." | |
| fi | |
| vp run dist:desktop:artifact "${args[@]}" | |
| - name: Collect release assets | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p release-publish | |
| shopt -s nullglob | |
| for pattern in \ | |
| "release/*.dmg" \ | |
| "release/*.zip" \ | |
| "release/*.AppImage" \ | |
| "release/*.exe" \ | |
| "release/*.blockmap" \ | |
| "release/*.yml"; do | |
| for file in $pattern; do | |
| cp "$file" release-publish/ | |
| done | |
| done | |
| if [[ "${{ matrix.platform }}" == "mac" && "${{ matrix.arch }}" != "arm64" ]]; then | |
| shopt -s nullglob | |
| for manifest in release-publish/*-mac.yml; do | |
| mv "$manifest" "${manifest%.yml}-${{ matrix.arch }}.yml" | |
| done | |
| fi | |
| # Enable if Windows arm64 builds are enabled. | |
| # Windows updater metadata is channel-specific (for example | |
| # "latest.yml" or "nightly.yml"). Suffix each per-arch copy so the | |
| # release job can merge matching arm64/x64 manifests back into one | |
| # canonical manifest per channel. | |
| # if [[ "${{ matrix.platform }}" == "win" ]]; then | |
| # shopt -s nullglob | |
| # for manifest in release-publish/*.yml; do | |
| # mv "$manifest" "${manifest%.yml}-win-${{ matrix.arch }}.yml" | |
| # done | |
| # fi | |
| - name: Collect resource monitor | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| binary_name="t3-resource-monitor" | |
| if [[ "${{ matrix.platform }}" == "win" ]]; then | |
| binary_name="${binary_name}.exe" | |
| fi | |
| source_path="native/resource-monitor/target/${{ matrix.rust_target }}/release/${binary_name}" | |
| target_dir="resource-monitor-publish/${{ matrix.resource_key }}" | |
| mkdir -p "$target_dir" | |
| cp "$source_path" "$target_dir/$binary_name" | |
| - name: Upload build artifacts | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: desktop-${{ matrix.platform }}-${{ matrix.arch }} | |
| path: release-publish/* | |
| if-no-files-found: error | |
| - name: Upload resource monitor | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: resource-monitor-${{ matrix.resource_key }} | |
| path: resource-monitor-publish/${{ matrix.resource_key }}/* | |
| if-no-files-found: error | |
| publish_cli: | |
| name: Publish CLI to npm | |
| needs: [preflight, relay_public_config, build] | |
| # Fork: do not publish the globally named upstream npm package from this repo. | |
| if: false | |
| runs-on: ubuntu-24.04 # blacksmith-8vcpu-ubuntu-2404 | |
| timeout-minutes: 10 | |
| permissions: | |
| contents: read | |
| id-token: write | |
| env: | |
| T3CODE_CLERK_PUBLISHABLE_KEY: ${{ needs.relay_public_config.outputs.clerk_publishable_key }} | |
| T3CODE_CLERK_JWT_TEMPLATE: ${{ needs.relay_public_config.outputs.clerk_jwt_template }} | |
| T3CODE_CLERK_CLI_OAUTH_CLIENT_ID: ${{ needs.relay_public_config.outputs.clerk_cli_oauth_client_id }} | |
| T3CODE_RELAY_URL: ${{ needs.relay_public_config.outputs.relay_url }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.preflight.outputs.ref }} | |
| sparse-checkout: | | |
| /* | |
| !/.repos/ | |
| sparse-checkout-cone-mode: false | |
| - name: Setup Vite+ | |
| uses: voidzero-dev/setup-vp@v1 | |
| with: | |
| node-version-file: package.json | |
| cache: true | |
| run-install: | | |
| args: | |
| - --filter=t3... | |
| - --filter=@t3tools/web... | |
| - --filter=@t3tools/scripts... | |
| - name: Download relay client tracing config | |
| continue-on-error: true | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: relay-client-tracing-config | |
| path: ${{ runner.temp }}/relay-client-tracing | |
| - name: Load relay client tracing config | |
| shell: bash | |
| run: | | |
| config_path="$RUNNER_TEMP/relay-client-tracing/relay-client-tracing.env" | |
| if [[ ! -f "$config_path" ]]; then | |
| echo "No relay client tracing config artifact; continuing without it." | |
| exit 0 | |
| fi | |
| tracing_token="$(sed -n 's/^T3CODE_RELAY_CLIENT_OTLP_TRACES_TOKEN=//p' "$config_path")" | |
| echo "::add-mask::$tracing_token" | |
| cat "$config_path" >> "$GITHUB_ENV" | |
| - name: Align package versions to release version | |
| run: node scripts/update-release-package-versions.ts "${{ needs.preflight.outputs.version }}" | |
| - name: Build web package | |
| run: vp run --filter @t3tools/web build | |
| - name: Build CLI package | |
| run: vp run --filter t3 build | |
| - name: Download resource monitors | |
| uses: actions/download-artifact@v8 | |
| with: | |
| pattern: resource-monitor-* | |
| path: ${{ runner.temp }}/resource-monitors | |
| - name: Bundle resource monitors into CLI package | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| for artifact_dir in "$RUNNER_TEMP"/resource-monitors/resource-monitor-*; do | |
| resource_key="${artifact_dir##*/resource-monitor-}" | |
| target_dir="apps/server/dist/resource-monitor/${resource_key}" | |
| mkdir -p "$target_dir" | |
| cp "$artifact_dir"/t3-resource-monitor* "$target_dir/" | |
| chmod +x "$target_dir"/t3-resource-monitor 2>/dev/null || true | |
| done | |
| - name: Publish CLI package | |
| run: node apps/server/scripts/cli.ts publish --tag "${{ needs.preflight.outputs.cli_dist_tag }}" --app-version "${{ needs.preflight.outputs.version }}" --verbose | |
| release: | |
| name: Publish GitHub Release | |
| needs: [preflight, build] | |
| if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.build.result == 'success' }} | |
| runs-on: ubuntu-24.04 # blacksmith-8vcpu-ubuntu-2404 | |
| timeout-minutes: 10 | |
| permissions: | |
| contents: write | |
| steps: | |
| # Prefer RELEASE_APP_* when configured; fall back to github.token | |
| # (upstream #5394) when those secrets are absent or minting fails. | |
| - id: app_token | |
| name: Mint release app token | |
| continue-on-error: true | |
| uses: actions/create-github-app-token@v2 | |
| with: | |
| app-id: ${{ secrets.RELEASE_APP_ID }} | |
| private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} | |
| owner: ${{ github.repository_owner }} | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.preflight.outputs.ref }} | |
| sparse-checkout: | | |
| /* | |
| !/.repos/ | |
| sparse-checkout-cone-mode: false | |
| - name: Setup Vite+ | |
| uses: voidzero-dev/setup-vp@v1 | |
| with: | |
| node-version-file: package.json | |
| cache: true | |
| run-install: | | |
| args: | |
| - --filter=@t3tools/scripts... | |
| - name: Download all desktop artifacts | |
| uses: actions/download-artifact@v8 | |
| with: | |
| pattern: desktop-* | |
| merge-multiple: true | |
| path: release-assets | |
| - name: Merge macOS updater manifests | |
| run: | | |
| set -euo pipefail | |
| shopt -s nullglob | |
| found_mac_manifest=false | |
| for x64_manifest in release-assets/*-mac-x64.yml; do | |
| arm64_manifest="${x64_manifest/-x64.yml/.yml}" | |
| if [[ ! -f "$arm64_manifest" ]]; then | |
| echo "Missing matching arm64 macOS manifest for $x64_manifest" >&2 | |
| exit 1 | |
| fi | |
| found_mac_manifest=true | |
| node scripts/merge-update-manifests.ts --platform mac \ | |
| "$arm64_manifest" \ | |
| "$x64_manifest" | |
| rm -f "$x64_manifest" | |
| done | |
| if [[ "$found_mac_manifest" != true ]]; then | |
| echo "No macOS updater manifests found to merge." >&2 | |
| exit 1 | |
| fi | |
| # - name: Merge Windows updater manifests | |
| # run: | | |
| # shopt -s nullglob | |
| # found_windows_manifest=false | |
| # for x64_manifest in release-assets/*-win-x64.yml; do | |
| # if [[ "$(basename "$x64_manifest")" == builder-debug-* ]]; then | |
| # continue | |
| # fi | |
| # arm64_manifest="${x64_manifest/-x64.yml/-arm64.yml}" | |
| # output_manifest="${x64_manifest/-win-x64.yml/.yml}" | |
| # if [[ ! -f "$arm64_manifest" ]]; then | |
| # echo "Missing matching arm64 Windows manifest for $x64_manifest" >&2 | |
| # exit 1 | |
| # fi | |
| # found_windows_manifest=true | |
| # node scripts/merge-update-manifests.ts --platform win \ | |
| # "$arm64_manifest" \ | |
| # "$x64_manifest" \ | |
| # "$output_manifest" | |
| # rm -f "$arm64_manifest" "$x64_manifest" | |
| # done | |
| # if [[ "$found_windows_manifest" != true ]]; then | |
| # echo "No Windows updater manifests found to merge." >&2 | |
| # exit 1 | |
| # fi | |
| - name: Publish release | |
| if: needs.preflight.outputs.previous_tag != '' | |
| uses: softprops/action-gh-release@v3 | |
| with: | |
| tag_name: ${{ needs.preflight.outputs.tag }} | |
| target_commitish: ${{ needs.preflight.outputs.ref }} | |
| name: ${{ needs.preflight.outputs.release_name }} | |
| repository: ${{ needs.preflight.outputs.release_repository }} | |
| generate_release_notes: true | |
| previous_tag: ${{ needs.preflight.outputs.previous_tag }} | |
| prerelease: ${{ needs.preflight.outputs.is_prerelease }} | |
| make_latest: ${{ needs.preflight.outputs.make_latest }} | |
| files: | | |
| release-assets/*.dmg | |
| release-assets/*.zip | |
| release-assets/*.AppImage | |
| release-assets/*.exe | |
| release-assets/*.blockmap | |
| release-assets/*.yml | |
| fail_on_unmatched_files: true | |
| token: ${{ steps.app_token.outputs.token || github.token }} | |
| - name: Publish first release | |
| if: needs.preflight.outputs.previous_tag == '' | |
| uses: softprops/action-gh-release@v3 | |
| with: | |
| tag_name: ${{ needs.preflight.outputs.tag }} | |
| target_commitish: ${{ needs.preflight.outputs.ref }} | |
| name: ${{ needs.preflight.outputs.release_name }} | |
| repository: ${{ needs.preflight.outputs.release_repository }} | |
| generate_release_notes: true | |
| prerelease: ${{ needs.preflight.outputs.is_prerelease }} | |
| make_latest: ${{ needs.preflight.outputs.make_latest }} | |
| files: | | |
| release-assets/*.dmg | |
| release-assets/*.zip | |
| release-assets/*.AppImage | |
| release-assets/*.exe | |
| release-assets/*.blockmap | |
| release-assets/*.yml | |
| fail_on_unmatched_files: true | |
| token: ${{ steps.app_token.outputs.token || github.token }} | |
| deploy_web: | |
| name: Deploy hosted web app | |
| needs: [preflight, relay_public_config, release] | |
| if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.relay_public_config.result == 'success' && needs.release.result == 'success' }} | |
| runs-on: ubuntu-24.04 # blacksmith-8vcpu-ubuntu-2404 | |
| timeout-minutes: 20 | |
| env: | |
| T3CODE_CLERK_PUBLISHABLE_KEY: ${{ needs.relay_public_config.outputs.clerk_publishable_key }} | |
| T3CODE_CLERK_JWT_TEMPLATE: ${{ needs.relay_public_config.outputs.clerk_jwt_template }} | |
| T3CODE_CLERK_CLI_OAUTH_CLIENT_ID: ${{ needs.relay_public_config.outputs.clerk_cli_oauth_client_id }} | |
| T3CODE_RELAY_URL: ${{ needs.relay_public_config.outputs.relay_url }} | |
| VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} | |
| VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} | |
| VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} | |
| T3CODE_WEB_ROUTER_URL: ${{ vars.T3CODE_WEB_ROUTER_URL }} | |
| T3CODE_WEB_LATEST_DOMAIN: ${{ vars.T3CODE_WEB_LATEST_DOMAIN }} | |
| T3CODE_WEB_NIGHTLY_DOMAIN: ${{ vars.T3CODE_WEB_NIGHTLY_DOMAIN }} | |
| VERCEL_TEAM_SLUG: ${{ vars.VERCEL_TEAM_SLUG }} | |
| steps: | |
| - id: vercel_config | |
| name: Check Vercel configuration | |
| env: | |
| VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} | |
| VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} | |
| VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} | |
| run: | | |
| if [[ -z "${VERCEL_TOKEN:-}" || -z "${VERCEL_ORG_ID:-}" || -z "${VERCEL_PROJECT_ID:-}" ]]; then | |
| echo "configured=false" >> "$GITHUB_OUTPUT" | |
| echo "Vercel secrets not configured; skipping hosted web deploy." | |
| else | |
| echo "configured=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Checkout | |
| if: steps.vercel_config.outputs.configured == 'true' | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.preflight.outputs.ref }} | |
| sparse-checkout: | | |
| /* | |
| !/.repos/ | |
| sparse-checkout-cone-mode: false | |
| - name: Setup Vite+ | |
| if: steps.vercel_config.outputs.configured == 'true' | |
| uses: voidzero-dev/setup-vp@v1 | |
| with: | |
| node-version-file: package.json | |
| cache: true | |
| run-install: | | |
| args: | |
| - --filter=@t3tools/scripts... | |
| - --filter=@t3tools/web... | |
| - name: Download relay client tracing config | |
| if: steps.vercel_config.outputs.configured == 'true' | |
| continue-on-error: true | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: relay-client-tracing-config | |
| path: ${{ runner.temp }}/relay-client-tracing | |
| - name: Load relay client tracing config | |
| if: steps.vercel_config.outputs.configured == 'true' | |
| shell: bash | |
| run: | | |
| config_path="$RUNNER_TEMP/relay-client-tracing/relay-client-tracing.env" | |
| if [[ ! -f "$config_path" ]]; then | |
| echo "No relay client tracing config artifact; continuing without it." | |
| exit 0 | |
| fi | |
| tracing_token="$(sed -n 's/^T3CODE_RELAY_CLIENT_OTLP_TRACES_TOKEN=//p' "$config_path")" | |
| echo "::add-mask::$tracing_token" | |
| cat "$config_path" >> "$GITHUB_ENV" | |
| - name: Align package versions to release version | |
| if: steps.vercel_config.outputs.configured == 'true' | |
| run: node scripts/update-release-package-versions.ts "${{ needs.preflight.outputs.version }}" | |
| - name: Refresh release lockfile | |
| if: steps.vercel_config.outputs.configured == 'true' | |
| run: vp install --lockfile-only --ignore-scripts | |
| - name: Deploy and alias channel | |
| id: deploy | |
| if: steps.vercel_config.outputs.configured == 'true' | |
| continue-on-error: false | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| router_url="${T3CODE_WEB_ROUTER_URL:-https://app.t3.codes}" | |
| latest_domain="${T3CODE_WEB_LATEST_DOMAIN:-latest.app.t3.codes}" | |
| nightly_domain="${T3CODE_WEB_NIGHTLY_DOMAIN:-nightly.app.t3.codes}" | |
| router_domain="${router_url#http://}" | |
| router_domain="${router_domain#https://}" | |
| router_domain="${router_domain%%/*}" | |
| if [[ "${{ needs.preflight.outputs.release_channel }}" == "stable" ]]; then | |
| channel_domain="$latest_domain" | |
| channel_name="latest" | |
| else | |
| channel_domain="$nightly_domain" | |
| channel_name="nightly" | |
| fi | |
| vercel_scope="${VERCEL_TEAM_SLUG:-$VERCEL_ORG_ID}" | |
| vercel_scope_args=(--scope "$vercel_scope") | |
| echo "Deploying hosted web app for $channel_name channel." | |
| deployment_url="$( | |
| vp dlx vercel@53.1.1 deploy \ | |
| --archive=tgz \ | |
| --prod \ | |
| --skip-domain \ | |
| --yes \ | |
| --token "$VERCEL_TOKEN" \ | |
| "${vercel_scope_args[@]}" \ | |
| --build-env "APP_VERSION=${{ needs.preflight.outputs.version }}" \ | |
| --build-env "T3CODE_CLERK_PUBLISHABLE_KEY=${T3CODE_CLERK_PUBLISHABLE_KEY:-}" \ | |
| --build-env "T3CODE_CLERK_JWT_TEMPLATE=${T3CODE_CLERK_JWT_TEMPLATE:-}" \ | |
| --build-env "T3CODE_CLERK_CLI_OAUTH_CLIENT_ID=${T3CODE_CLERK_CLI_OAUTH_CLIENT_ID:-}" \ | |
| --build-env "T3CODE_RELAY_URL=${T3CODE_RELAY_URL:-}" \ | |
| --build-env "T3CODE_RELAY_CLIENT_OTLP_TRACES_URL=${T3CODE_RELAY_CLIENT_OTLP_TRACES_URL:-}" \ | |
| --build-env "T3CODE_RELAY_CLIENT_OTLP_TRACES_DATASET=${T3CODE_RELAY_CLIENT_OTLP_TRACES_DATASET:-}" \ | |
| --build-env "T3CODE_RELAY_CLIENT_OTLP_TRACES_TOKEN=${T3CODE_RELAY_CLIENT_OTLP_TRACES_TOKEN:-}" \ | |
| --build-env "VITE_HOSTED_APP_URL=$router_url" \ | |
| --build-env "VITE_HOSTED_APP_CHANNEL=$channel_name" | |
| )" | |
| echo "Aliasing $deployment_url to $channel_domain." | |
| vp dlx vercel@53.1.1 alias set "$deployment_url" "$channel_domain" \ | |
| --token "$VERCEL_TOKEN" \ | |
| "${vercel_scope_args[@]}" | |
| if [[ "$channel_name" == "latest" && -n "$router_domain" && "$router_domain" != "$channel_domain" ]]; then | |
| echo "Aliasing $deployment_url to router domain $router_domain." | |
| vp dlx vercel@53.1.1 alias set "$deployment_url" "$router_domain" \ | |
| --token "$VERCEL_TOKEN" \ | |
| "${vercel_scope_args[@]}" | |
| fi | |
| finalize: | |
| name: Finalize release | |
| if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.release.result == 'success' && needs.preflight.outputs.release_channel == 'stable' }} | |
| needs: [preflight, release] | |
| runs-on: ubuntu-24.04 # blacksmith-8vcpu-ubuntu-2404 | |
| timeout-minutes: 10 | |
| permissions: | |
| contents: write | |
| steps: | |
| # Prefer RELEASE_APP_* when configured; fall back to github.token | |
| # (upstream #5394) when those secrets are absent or minting fails. | |
| - id: app_token | |
| name: Mint release app token | |
| continue-on-error: true | |
| uses: actions/create-github-app-token@v2 | |
| with: | |
| app-id: ${{ secrets.RELEASE_APP_ID }} | |
| private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} | |
| owner: ${{ github.repository_owner }} | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: main | |
| fetch-depth: 0 | |
| token: ${{ steps.app_token.outputs.token || github.token }} | |
| persist-credentials: true | |
| sparse-checkout: | | |
| /* | |
| !/.repos/ | |
| sparse-checkout-cone-mode: false | |
| - id: app_bot | |
| name: Resolve commit identity | |
| env: | |
| GH_TOKEN: ${{ steps.app_token.outputs.token || github.token }} | |
| APP_SLUG: ${{ steps.app_token.outputs.app-slug }} | |
| run: | | |
| set -euo pipefail | |
| if [[ -n "${APP_SLUG:-}" ]]; then | |
| user_id="$(gh api "/users/${APP_SLUG}[bot]" --jq .id)" | |
| echo "name=${APP_SLUG}[bot]" >> "$GITHUB_OUTPUT" | |
| echo "email=${user_id}+${APP_SLUG}[bot]@users.noreply.github.com" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "RELEASE_APP token unavailable; using github-actions identity." | |
| echo "name=github-actions[bot]" >> "$GITHUB_OUTPUT" | |
| echo "email=41898282+github-actions[bot]@users.noreply.github.com" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Setup Vite+ | |
| uses: voidzero-dev/setup-vp@v1 | |
| with: | |
| node-version-file: package.json | |
| cache: true | |
| run-install: | | |
| args: | |
| - --filter=@t3tools/scripts... | |
| - --filter=@t3tools/oxlint-plugin-t3code... | |
| - id: update_versions | |
| name: Update version strings | |
| env: | |
| RELEASE_VERSION: ${{ needs.preflight.outputs.version }} | |
| run: node scripts/update-release-package-versions.ts "$RELEASE_VERSION" --github-output | |
| - name: Format package.json files | |
| if: steps.update_versions.outputs.changed == 'true' | |
| run: vp fmt apps/server/package.json apps/desktop/package.json apps/web/package.json packages/contracts/package.json | |
| - name: Refresh lockfile | |
| if: steps.update_versions.outputs.changed == 'true' | |
| run: vp install --lockfile-only --ignore-scripts | |
| - name: Commit and push version bump | |
| if: steps.update_versions.outputs.changed == 'true' | |
| shell: bash | |
| env: | |
| RELEASE_TAG: ${{ needs.preflight.outputs.tag }} | |
| run: | | |
| if git diff --quiet -- apps/server/package.json apps/desktop/package.json apps/web/package.json packages/contracts/package.json pnpm-lock.yaml; then | |
| echo "No version changes to commit." | |
| exit 0 | |
| fi | |
| git config user.name "${{ steps.app_bot.outputs.name }}" | |
| 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 push origin HEAD:main | |
| announce_discord: | |
| name: Announce release on Discord | |
| if: | | |
| always() && !cancelled() && | |
| needs.preflight.result == 'success' && | |
| needs.relay_public_config.result == 'success' && | |
| needs.release.result == 'success' && | |
| needs.deploy_web.result == 'success' && | |
| (needs.finalize.result == 'success' || needs.finalize.result == 'skipped') | |
| needs: [preflight, relay_public_config, release, deploy_web, finalize] | |
| runs-on: ubuntu-24.04 # blacksmith-8vcpu-ubuntu-2404 | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.preflight.outputs.ref }} | |
| sparse-checkout: | | |
| /* | |
| !/.repos/ | |
| sparse-checkout-cone-mode: false | |
| - name: Setup Vite+ | |
| uses: voidzero-dev/setup-vp@v1 | |
| with: | |
| node-version-file: package.json | |
| cache: true | |
| run-install: | | |
| args: | |
| - --filter=@t3tools/scripts... | |
| - name: Announce prerelease on Discord | |
| if: needs.preflight.outputs.is_prerelease == 'true' | |
| continue-on-error: true | |
| env: | |
| DISCORD_MENTION_ROLE_ID: ${{ secrets.DISCORD_RELEASE_NIGHTLY_ROLE_ID }} | |
| DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_RELEASE_WEBHOOK_URL }} | |
| run: | | |
| node scripts/notify-discord-release.ts prerelease \ | |
| --role-id "$DISCORD_MENTION_ROLE_ID" \ | |
| --release-name "${{ needs.preflight.outputs.release_name }}" \ | |
| --release-version "${{ needs.preflight.outputs.version }}" \ | |
| --tag "${{ needs.preflight.outputs.tag }}" \ | |
| --release-url "https://github.com/${{ github.repository }}/releases/tag/${{ needs.preflight.outputs.tag }}" | |
| - name: Announce latest release on Discord | |
| if: needs.preflight.outputs.make_latest == 'true' | |
| continue-on-error: true | |
| env: | |
| DISCORD_MENTION_ROLE_ID: ${{ secrets.DISCORD_RELEASE_LATEST_ROLE_ID }} | |
| DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_RELEASE_WEBHOOK_URL }} | |
| run: | | |
| node scripts/notify-discord-release.ts latest \ | |
| --role-id "$DISCORD_MENTION_ROLE_ID" \ | |
| --release-name "${{ needs.preflight.outputs.release_name }}" \ | |
| --release-version "${{ needs.preflight.outputs.version }}" \ | |
| --tag "${{ needs.preflight.outputs.tag }}" \ | |
| --release-url "https://github.com/${{ github.repository }}/releases/tag/${{ needs.preflight.outputs.tag }}" |