Skip to content

Commit c3e0ee3

Browse files
authored
Build analytics-cli for Windows x64 (#822)
1. Build CLI for Windows x64 with gnu stack 2. Add smoke tests and artifact uploads for windows builds of analytics-cli.exe 3. confirm binary works correctly on actual windows machine. CLI is actually build cross tooling on linux machine Smoke Tests passing on all platforms (including windows) : https://github.com/trunk-io/analytics-cli/actions/runs/19517735811
1 parent ee3daf4 commit c3e0ee3

File tree

16 files changed

+385
-135
lines changed

16 files changed

+385
-135
lines changed

.cargo/config.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
[target.x86_64-pc-windows-msvc]
2-
linker = "rust-lld.exe"
1+
[target.x86_64-pc-windows-gnu]
2+
linker = "x86_64-w64-mingw32-gcc"
33
rustflags = []
44
[target.x86_64-apple-darwin]
55
rustflags = []

.github/actions/build_cli_linux_target/action.yaml

Lines changed: 0 additions & 55 deletions
This file was deleted.
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
name: Build CLI Unix/Windows Target
2+
description: Build release binary for Unix and Windows targets (cross-compiled on Linux runners).
3+
4+
inputs:
5+
target:
6+
description: Rust target triple, e.g. x86_64-unknown-linux-musl
7+
profile:
8+
description: Rust profile to build, e.g. release-with-debug
9+
force-sentry-dev:
10+
description: Force sentry to use a devenv (set to true for testing, but not for releases)
11+
default: "false"
12+
13+
runs:
14+
using: composite
15+
steps:
16+
- name: Install musl deps
17+
shell: bash
18+
if: ${{ contains(inputs.target, 'musl') }}
19+
run: |
20+
sudo apt-get update
21+
sudo apt-get install -y musl-tools
22+
23+
- name: Build CLI ${{ inputs.target }} target
24+
uses: ./.github/actions/build_cli_target
25+
with:
26+
target: ${{ inputs.target }}
27+
profile: ${{ inputs.profile }}
28+
force-sentry-dev: ${{ inputs.force-sentry-dev }}
29+
30+
- name: Strip debug info
31+
shell: bash
32+
run: |
33+
if [[ "${{ inputs.target }}" == *"windows-gnu"* ]]; then
34+
# Windows binaries use .exe extension
35+
x86_64-w64-mingw32-strip target/${{ inputs.target }}/${{ inputs.profile }}/trunk-analytics-cli.exe
36+
else
37+
strip -s target/${{ inputs.target }}/${{ inputs.profile }}/trunk-analytics-cli
38+
fi
39+
40+
- name: Install upx
41+
shell: bash
42+
run: |
43+
sudo apt-get install -y upx
44+
45+
- name: Optimize CLI binary
46+
uses: ./.github/actions/optimize_cli_binary
47+
with:
48+
target: ${{ inputs.target }}
49+
profile: ${{ inputs.profile }}
50+
51+
- name: Check dynamic deps (Unix)
52+
shell: bash
53+
if: ${{ inputs.target != 'x86_64-unknown-illumos' && !contains(inputs.target, 'windows') }}
54+
run: |
55+
# Confirm that the binary is statically linked
56+
set +e
57+
ldd target/${{ inputs.target }}/${{ inputs.profile }}/trunk-analytics-cli
58+
if [ $? -ne 1 ]; then
59+
echo "trunk-analytics-cli has dynamic deps - we expect it to be statically linked"
60+
exit 1
61+
fi
62+
exit 0
63+
64+
- name: Check dynamic deps (Windows)
65+
shell: bash
66+
if: ${{ contains(inputs.target, 'windows-gnu') }}
67+
run: |
68+
# Check DLL imports using objdump (Windows equivalent of ldd)
69+
# A statically linked binary should only import system DLLs (kernel32, msvcrt, etc.)
70+
# and not application-specific DLLs
71+
set +e
72+
imports=$(x86_64-w64-mingw32-objdump -p target/${{ inputs.target }}/${{ inputs.profile }}/trunk-analytics-cli.exe 2>/dev/null | grep -i "DLL Name:" | awk '{print $3}' | tr '\n' ' ')
73+
echo "DLL imports: $imports"
74+
75+
# Check for unexpected DLLs (excluding standard Windows system DLLs)
76+
# Standard system DLLs that are expected: KERNEL32, MSVCRT, etc.
77+
unexpected_dlls=$(echo "$imports" | grep -ivE "(KERNEL32|MSVCRT|MSVCP|API-MS-WIN|NTDLL|ADVAPI32|USER32|GDI32|SHELL32|OLE32|OLEAUT32|WS2_32|WINHTTP|CRYPT32|SHLWAPI)" || true)
78+
79+
if [ -n "$unexpected_dlls" ]; then
80+
echo "trunk-analytics-cli.exe has unexpected dynamic dependencies: $unexpected_dlls"
81+
echo "We expect it to be statically linked (only system DLLs should be imported)"
82+
exit 1
83+
fi
84+
85+
echo "Binary appears to be statically linked (only system DLLs imported)"
86+
exit 0

.github/actions/build_cli_target/action.yaml

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,31 @@ runs:
2020
shell: bash
2121
run: rustup target add ${{ inputs.target }} --toolchain ${{ inputs.toolchain }}
2222

23+
- name: Install MinGW for Windows cross-compilation
24+
shell: bash
25+
if: ${{ contains(inputs.target, 'windows-gnu') }}
26+
run: |
27+
sudo apt-get update
28+
# Install build-essential for host build scripts (cc/gcc)
29+
# Install gcc-mingw-w64-x86-64 for Windows target cross-compilation
30+
sudo apt-get install -y build-essential gcc-mingw-w64-x86-64
31+
2332
- name: Install cross
2433
shell: bash
2534
run: cargo install cross --git https://github.com/cross-rs/cross --force
2635

36+
- name: Build CLI ${{ inputs.target }} target (Windows GNU native)
37+
shell: bash
38+
# Build Windows GNU natively without Docker since we have MinGW installed
39+
if: ${{ contains(inputs.target, 'windows-gnu') }}
40+
run: |
41+
cargo +${{ inputs.toolchain }} build -p trunk-analytics-cli --profile=${{ inputs.profile }} --target=${{ inputs.target }} ${{ (inputs.force-sentry-dev == 'true' && '--features force-sentry-env-dev') || '' }}
42+
2743
- name: Build CLI ${{ inputs.target }} target
28-
uses: houseabsolute/[email protected].1
44+
uses: houseabsolute/[email protected].5
2945
# Cross-rs does not directly support cross compilation with macOS targets
30-
if: ${{ !contains(inputs.target, 'darwin') }}
46+
# Use action for non-Windows-GNU targets
47+
if: ${{ !contains(inputs.target, 'darwin') && !contains(inputs.target, 'windows-gnu') }}
3148
with:
3249
command: build
3350
target: ${{ inputs.target }}
@@ -45,4 +62,10 @@ runs:
4562
4663
- name: Create binary with debug info
4764
shell: bash
48-
run: cp target/${{ inputs.target }}/${{ inputs.profile }}/trunk-analytics-cli target/${{ inputs.target }}/${{ inputs.profile }}/trunk-analytics-cli.debug
65+
run: |
66+
# Windows binaries have .exe extension, Unix binaries don't
67+
if [[ "${{ inputs.target }}" == *"windows-gnu"* ]]; then
68+
cp target/${{ inputs.target }}/${{ inputs.profile }}/trunk-analytics-cli.exe target/${{ inputs.target }}/${{ inputs.profile }}/trunk-analytics-cli.debug.exe
69+
else
70+
cp target/${{ inputs.target }}/${{ inputs.profile }}/trunk-analytics-cli target/${{ inputs.target }}/${{ inputs.profile }}/trunk-analytics-cli.debug
71+
fi

.github/actions/build_cli_tools/action.yaml

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ runs:
2020
shell: bash
2121
run: rustup target add ${{ inputs.target }}
2222

23+
- name: Install MinGW for Windows cross-compilation
24+
shell: bash
25+
if: ${{ contains(inputs.target, 'windows-gnu') }}
26+
run: |
27+
sudo apt-get update
28+
# Install build-essential for host build scripts (cc/gcc)
29+
# Install gcc-mingw-w64-x86-64 for Windows target cross-compilation
30+
sudo apt-get install -y build-essential gcc-mingw-w64-x86-64
31+
2332
- name: Install musl development tools
2433
shell: bash
2534
run: |
@@ -28,13 +37,19 @@ runs:
2837
sudo apt-get install -y musl-tools
2938
fi
3039
31-
- name: Install cross for CLI tools
40+
- name: Determine build tool
41+
id: build-tool
3242
shell: bash
3343
run: |
34-
# Only install cross if we're not on the target architecture
35-
if [[ "${{ inputs.target }}" == *"aarch64"* ]] && [[ "$(uname -m)" == "aarch64" ]]; then
36-
echo "Skipping cross installation - already on target architecture"
44+
# Determine if we should use cargo (native) or cross (Docker)
45+
# Use cargo for: darwin, native aarch64, Windows GNU
46+
# Use cross for: everything else (cross-compilation)
47+
if [[ "${{ inputs.os-name }}" == *"darwin"* ]] || \
48+
[[ "${{ inputs.target }}" == *"aarch64"* && "$(uname -m)" == "aarch64" ]] || \
49+
[[ "${{ inputs.target }}" == *"windows-gnu"* ]]; then
50+
echo "BUILD_TOOL=cargo" >> $GITHUB_ENV
3751
else
52+
echo "BUILD_TOOL=cross" >> $GITHUB_ENV
3853
cargo install cross --version 0.2.5 --force
3954
fi
4055
@@ -62,26 +77,14 @@ runs:
6277
- name: Build Bundle CLI
6378
shell: bash
6479
run: |
65-
if [[ "${{ inputs.os-name }}" == *"darwin"* ]] || [[ "${{ inputs.target }}" == *"aarch64"* && "$(uname -m)" == "aarch64" ]]; then
66-
cargo build -p bundle --profile=release-with-debug --target=${{ inputs.target }} --features build-binary
67-
else
68-
cross build -p bundle --profile=release-with-debug --target=${{ inputs.target }} --features build-binary
69-
fi
80+
${{ env.BUILD_TOOL }} build -p bundle --profile=release-with-debug --target=${{ inputs.target }} --features build-binary
7081
7182
- name: Build XCResult CLI
7283
shell: bash
7384
run: |
74-
if [[ "${{ inputs.os-name }}" == *"darwin"* ]] || [[ "${{ inputs.target }}" == *"aarch64"* && "$(uname -m)" == "aarch64" ]]; then
75-
cargo build -p xcresult --profile=release-with-debug --target=${{ inputs.target }}
76-
else
77-
cross build -p xcresult --profile=release-with-debug --target=${{ inputs.target }}
78-
fi
85+
${{ env.BUILD_TOOL }} build -p xcresult --profile=release-with-debug --target=${{ inputs.target }}
7986
8087
- name: Build Codeowners CLI
8188
shell: bash
8289
run: |
83-
if [[ "${{ inputs.os-name }}" == *"darwin"* ]] || [[ "${{ inputs.target }}" == *"aarch64"* && "$(uname -m)" == "aarch64" ]]; then
84-
cargo build -p codeowners --profile=release-with-debug --target=${{ inputs.target }}
85-
else
86-
cross build -p codeowners --profile=release-with-debug --target=${{ inputs.target }}
87-
fi
90+
${{ env.BUILD_TOOL }} build -p codeowners --profile=release-with-debug --target=${{ inputs.target }}

.github/actions/optimize_cli_binary/action.yaml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
name: Optimize CLI Binary
2+
description: Compress CLI binary using UPX
23

34
inputs:
45
target:
@@ -12,8 +13,18 @@ runs:
1213
- name: Compress binary
1314
shell: bash
1415
run: |
15-
upx --best --lzma target/${{ inputs.target }}/${{ inputs.profile }}/trunk-analytics-cli
16+
if [[ "${{ inputs.target }}" == *"windows-gnu"* ]]; then
17+
# Windows binaries use .exe extension
18+
upx --best --lzma target/${{ inputs.target }}/${{ inputs.profile }}/trunk-analytics-cli.exe
19+
else
20+
upx --best --lzma target/${{ inputs.target }}/${{ inputs.profile }}/trunk-analytics-cli
21+
fi
1622
1723
- name: Report binary size
1824
shell: bash
19-
run: ls -lh target/${{ inputs.target }}/${{ inputs.profile }}/trunk-analytics-cli
25+
run: |
26+
if [[ "${{ inputs.target }}" == *"windows-gnu"* ]]; then
27+
ls -lh target/${{ inputs.target }}/${{ inputs.profile }}/trunk-analytics-cli.exe
28+
else
29+
ls -lh target/${{ inputs.target }}/${{ inputs.profile }}/trunk-analytics-cli
30+
fi

.github/actions/perform_gitlab_smoke_test/action.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ runs:
4242
run: |
4343
./${{ inputs.cli-binary-location }} test \
4444
--org-url-slug trunk-staging-org \
45-
--junit-paths ${{ github.workspace }}/target/**/*junit.xml \
45+
--junit-paths "${{ github.workspace }}/target/**/*junit.xml" \
4646
--token ${{ inputs.staging-api-token }} \
4747
--repo-url https://gitlab.com/trunk-staging-org/analytics-cli-fake-gitlab/ \
4848
cargo nextest run -p smoke-test --profile ci
@@ -68,7 +68,7 @@ runs:
6868
run: |
6969
./${{ inputs.cli-binary-location }} test \
7070
--org-url-slug trunk \
71-
--junit-paths ${{ github.workspace }}/target/**/*junit.xml \
71+
--junit-paths "${{ github.workspace }}/target/**/*junit.xml" \
7272
--token ${{ inputs.production-api-token }} \
7373
--repo-url https://gitlab.com/trunk-io/analytics-cli-fake-gitlab/ \
7474
cargo nextest run -p smoke-test --profile ci
@@ -93,7 +93,7 @@ runs:
9393
run: |
9494
./${{ inputs.cli-binary-location }} upload \
9595
--org-url-slug trunk-staging-org \
96-
--junit-paths ${{ github.workspace }}/target/**/*junit.xml \
96+
--junit-paths "${{ github.workspace }}/target/**/*junit.xml" \
9797
--token ${{ inputs.staging-api-token }} \
9898
--repo-url https://gitlab.com/trunk-staging-org/analytics-cli-fake-gitlab/
9999
@@ -117,6 +117,6 @@ runs:
117117
run: |
118118
./${{ inputs.cli-binary-location }} upload \
119119
--org-url-slug trunk \
120-
--junit-paths ${{ github.workspace }}/target/**/*junit.xml \
120+
--junit-paths "${{ github.workspace }}/target/**/*junit.xml" \
121121
--token ${{ inputs.production-api-token }} \
122122
--repo-url https://gitlab.com/trunk-io/analytics-cli-fake-gitlab/

0 commit comments

Comments
 (0)