Files
NexaVPN/.gitea/workflows/windows-desktop-client.yml
nessi 10dbd186ed feat: add code signing support for Windows desktop client executables and installers
Add osslsigncode dependency and implement three-step signing workflow. Decode base64-encoded PFX certificate from secrets, sign bundled tunnel helper before build, then sign desktop executable and NSIS installer after build. Use configurable timestamp URL with DigiCert fallback. Clean up certificate file in always-run step to prevent secret leakage.
2026-03-18 12:28:53 +01:00

138 lines
4.5 KiB
YAML

name: Build Windows Desktop Client
on:
workflow_dispatch:
jobs:
build-windows-client:
name: Build Windows Client
runs-on: ubuntu-latest
defaults:
run:
shell: bash
working-directory: desktop-client
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "22"
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: x86_64-pc-windows-msvc
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
build-essential \
clang \
lld \
llvm \
curl \
wget \
file \
nsis \
osslsigncode \
pkg-config \
libssl-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
patchelf
- name: Install cargo-xwin
run: |
if ! cargo xwin --version >/dev/null 2>&1; then
cargo install --locked cargo-xwin
fi
- name: Install desktop client dependencies
run: npm install
- name: Build bundled Windows tunnel helper
run: npm run helper:windows-x64
- name: Prepare signing certificate
if: ${{ secrets.WINDOWS_SIGN_PFX_B64 != '' && secrets.WINDOWS_SIGN_PFX_PASSWORD != '' }}
run: |
printf '%s' "${{ secrets.WINDOWS_SIGN_PFX_B64 }}" | base64 -d > /tmp/nexavpn-signing-cert.pfx
- name: Sign bundled Windows tunnel helper
if: ${{ secrets.WINDOWS_SIGN_PFX_B64 != '' && secrets.WINDOWS_SIGN_PFX_PASSWORD != '' }}
run: |
set -euo pipefail
timestamp_url="${{ secrets.WINDOWS_SIGN_TIMESTAMP_URL }}"
if [ -z "${timestamp_url}" ]; then
timestamp_url="http://timestamp.digicert.com"
fi
helper_path="src-tauri/bundled/windows-x64/nexavpn-tunnel-helper.exe"
signed_path="${helper_path}.signed"
osslsigncode sign \
-pkcs12 /tmp/nexavpn-signing-cert.pfx \
-pass "${{ secrets.WINDOWS_SIGN_PFX_PASSWORD }}" \
-h sha256 \
-ts "${timestamp_url}" \
-in "${helper_path}" \
-out "${signed_path}"
mv "${signed_path}" "${helper_path}"
- name: Build Windows installer
run: npm run tauri:build:windows-x64:linux
- name: Sign Windows desktop executable and installer
if: ${{ secrets.WINDOWS_SIGN_PFX_B64 != '' && secrets.WINDOWS_SIGN_PFX_PASSWORD != '' }}
run: |
set -euo pipefail
timestamp_url="${{ secrets.WINDOWS_SIGN_TIMESTAMP_URL }}"
if [ -z "${timestamp_url}" ]; then
timestamp_url="http://timestamp.digicert.com"
fi
sign_file() {
local input_path="$1"
local output_path="${input_path}.signed"
osslsigncode sign \
-pkcs12 /tmp/nexavpn-signing-cert.pfx \
-pass "${{ secrets.WINDOWS_SIGN_PFX_PASSWORD }}" \
-h sha256 \
-ts "${timestamp_url}" \
-in "${input_path}" \
-out "${output_path}"
mv "${output_path}" "${input_path}"
}
sign_file "src-tauri/target/x86_64-pc-windows-msvc/release/nexavpn-desktop.exe"
shopt -s nullglob
for installer in src-tauri/target/x86_64-pc-windows-msvc/release/bundle/nsis/*.exe; do
sign_file "${installer}"
done
- name: Cleanup signing certificate
if: ${{ always() && secrets.WINDOWS_SIGN_PFX_B64 != '' && secrets.WINDOWS_SIGN_PFX_PASSWORD != '' }}
run: rm -f /tmp/nexavpn-signing-cert.pfx
- name: Upload Windows installer
uses: christopherhx/gitea-upload-artifact@v4
with:
name: NexaVPN-windows-installer
path: |
desktop-client/src-tauri/target/x86_64-pc-windows-msvc/release/bundle/nsis/*.exe
desktop-client/src-tauri/target/x86_64-pc-windows-msvc/release/bundle/nsis/*.msi
if-no-files-found: error
- name: Upload raw Windows build outputs
uses: christopherhx/gitea-upload-artifact@v4
with:
name: NexaVPN-windows-raw-build
path: |
desktop-client/src-tauri/target/x86_64-pc-windows-msvc/release/nexavpn-desktop.exe
desktop-client/src-tauri/bundled/windows-x64/nexavpn-tunnel-helper.exe
if-no-files-found: error