Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 53 additions & 44 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,11 @@ jobs:
platform: win
target: nsis
arch: x64
# - label: Windows arm64
# runner: windows-11-arm
# platform: win
# target: nsis
# arch: arm64
- label: Windows arm64
runner: windows-11-arm
platform: win
target: nsis
arch: arm64
steps:
- name: Checkout
uses: actions/checkout@v6
Expand Down Expand Up @@ -242,10 +242,20 @@ jobs:
$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
$spectreComponents = @(
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64.Spectre"
)
if ("${{ matrix.arch }}" -eq "arm64") {
$spectreComponents += "Microsoft.VisualStudio.Component.VC.Tools.ARM64.Spectre"
}
$setupArgs = @("modify", "--installPath", "`"$installPath`"")
foreach ($component in $spectreComponents) {
$setupArgs += "--add"
$setupArgs += $component
}
$setupArgs += "--quiet"
$setupArgs += "--norestart"
$proc = Start-Process -FilePath $setupExe -ArgumentList $setupArgs -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"
Expand Down Expand Up @@ -403,17 +413,16 @@ jobs:
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
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: Upload build artifacts
uses: actions/upload-artifact@v7
Expand Down Expand Up @@ -514,34 +523,34 @@ jobs:
fi
done

# - 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: 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/-win-x64.yml/-win-arm64.yml}"
output_manifest="${x64_manifest/-win-x64.yml/.yml}"
if [[ ! -f "$arm64_manifest" ]]; then
echo "No matching arm64 Windows manifest for $x64_manifest; keeping x64-only manifest."
mv "$x64_manifest" "$output_manifest"
continue
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 manifest pairs found to merge."
fi

- name: Publish release
if: needs.preflight.outputs.previous_tag != ''
Expand Down
93 changes: 93 additions & 0 deletions .github/workflows/windows-arm64-app.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: Windows ARM64 App

on:
workflow_dispatch:
inputs:
sync_upstream:
description: "Merge upstream main before building"
required: false
default: true
type: boolean
schedule:
- cron: "25 */6 * * *"

permissions:
contents: write

concurrency:
group: windows-arm64-app
cancel-in-progress: false

jobs:
build_release:
name: Build and publish Windows ARM64 installer
runs-on: windows-11-arm
timeout-minutes: 60
steps:
- name: Checkout fork branch
uses: actions/checkout@v6
with:
ref: windows-arm64-support
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Merge upstream main
if: github.event_name == 'schedule' || github.event.inputs.sync_upstream != 'false'
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git remote add upstream https://github.com/pingdotgg/t3code.git
git fetch upstream main --tags
git merge --no-edit upstream/main
git push origin HEAD:windows-arm64-support

- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version-file: package.json

- name: Setup Node
uses: actions/setup-node@v6
with:
node-version-file: package.json

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Build Windows ARM64 installer
shell: pwsh
env:
T3CODE_DESKTOP_UPDATE_REPOSITORY: ${{ github.repository }}
run: |
$ErrorActionPreference = "Stop"
bun run dist:desktop:win:arm64 -- --verbose

- name: Resolve release metadata
id: meta
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$pkg = Get-Content apps/server/package.json | ConvertFrom-Json
$version = [string]$pkg.version
"version=$version" >> $env:GITHUB_OUTPUT
"tag=v$version-windows-arm64" >> $env:GITHUB_OUTPUT
"name=T3 Code Windows ARM64 v$version" >> $env:GITHUB_OUTPUT

- name: Publish fork release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.meta.outputs.tag }}
target_commitish: windows-arm64-support
name: ${{ steps.meta.outputs.name }}
body: |
Windows ARM64 fork build.

Built from `${{ github.repository }}@${{ github.sha }}`.
Upstream sync source: `pingdotgg/t3code@main`.
make_latest: false
files: |
release/*.exe
release/*.blockmap
release/*.yml
1 change: 1 addition & 0 deletions apps/desktop/src/app/DesktopAppIdentity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const defaultEnvironmentInput = {
isPackaged: true,
resourcesPath: "/Applications/T3 Code.app/Contents/Resources",
runningUnderArm64Translation: false,
windowsProcessorArchitectures: [],
} satisfies DesktopEnvironment.MakeDesktopEnvironmentInput;

type TestEnvironmentInput = Partial<DesktopEnvironment.MakeDesktopEnvironmentInput> & {
Expand Down
102 changes: 102 additions & 0 deletions apps/desktop/src/app/DesktopEnvironment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const defaultInput = {
isPackaged: false,
resourcesPath: "/Applications/T3 Code.app/Contents/Resources",
runningUnderArm64Translation: false,
windowsProcessorArchitectures: [],
} satisfies DesktopEnvironment.MakeDesktopEnvironmentInput;

const makeEnvironmentLayer = (
Expand Down Expand Up @@ -112,4 +113,105 @@ describe("DesktopEnvironment", () => {
);
}),
);

it.effect("detects macOS arm64 host running x64 build under Rosetta", () =>
Effect.gen(function* () {
const environment = yield* makeEnvironment({
platform: "darwin",
processArch: "x64",
runningUnderArm64Translation: true,
});

assert.deepEqual(environment.runtimeInfo, {
hostArch: "arm64",
appArch: "x64",
runningUnderArm64Translation: true,
});
}),
);

it.effect("detects Windows arm64 host via PROCESSOR_ARCHITECTURE env var", () =>
Effect.gen(function* () {
const environment = yield* makeEnvironment({
platform: "win32",
processArch: "x64",
runningUnderArm64Translation: false,
windowsProcessorArchitectures: ["ARM64"],
});

assert.deepEqual(environment.runtimeInfo, {
hostArch: "arm64",
appArch: "x64",
runningUnderArm64Translation: true,
});
}),
);

it.effect("detects Windows arm64 host via PROCESSOR_ARCHITEW6432 env var", () =>
Effect.gen(function* () {
const environment = yield* makeEnvironment({
platform: "win32",
processArch: "x64",
runningUnderArm64Translation: false,
windowsProcessorArchitectures: ["AMD64", "ARM64"],
Comment thread
cursor[bot] marked this conversation as resolved.
});

assert.deepEqual(environment.runtimeInfo, {
hostArch: "arm64",
appArch: "x64",
runningUnderArm64Translation: true,
});
}),
);

it.effect("reports x64 host on Windows x64 with x64 process", () =>
Effect.gen(function* () {
const environment = yield* makeEnvironment({
platform: "win32",
processArch: "x64",
runningUnderArm64Translation: false,
windowsProcessorArchitectures: ["AMD64"],
});

assert.deepEqual(environment.runtimeInfo, {
hostArch: "x64",
appArch: "x64",
runningUnderArm64Translation: false,
});
}),
);

it.effect("reports arm64 host and app on Windows arm64 native build", () =>
Effect.gen(function* () {
const environment = yield* makeEnvironment({
platform: "win32",
processArch: "arm64",
runningUnderArm64Translation: false,
windowsProcessorArchitectures: ["ARM64"],
});

assert.deepEqual(environment.runtimeInfo, {
hostArch: "arm64",
appArch: "arm64",
runningUnderArm64Translation: false,
});
}),
);

it.effect("reports no ARM64 translation on Linux", () =>
Effect.gen(function* () {
const environment = yield* makeEnvironment({
platform: "linux",
processArch: "x64",
runningUnderArm64Translation: false,
windowsProcessorArchitectures: [],
});

assert.deepEqual(environment.runtimeInfo, {
hostArch: "x64",
appArch: "x64",
runningUnderArm64Translation: false,
});
}),
);
});
Loading