name: OneClient Nightly (Unsigned) on: push: branches: [main] workflow_dispatch: concurrency: group: oneclient-nightly-${{ github.ref }} cancel-in-progress: true # Nightlies are unsigned test builds, so they do not need the release profile's # whole-program optimization - and that optimization is exactly what makes them # slow. `lto = "fat"` + `codegen-units = 1` force a single-threaded re-optimization # of the entire dependency graph at link time, on every run, which no cache can # ever skip. Thin LTO keeps nearly all of the runtime benefit at a fraction of the # link cost, and 16 codegen units lets the whole compile use every runner core. # # These are cargo's documented profile overrides, so `target/release` stays the # output directory and every path below is unaffected. The shipped release binary # is untouched: oneclient_release.yml still builds the fat-LTO profile from # Cargo.toml. That difference is why the two workflows must not share a cache # namespace - see `cache-namespace` on the Setup Rust step. env: CARGO_PROFILE_RELEASE_LTO: thin CARGO_PROFILE_RELEASE_CODEGEN_UNITS: '16' jobs: build: permissions: contents: read strategy: fail-fast: false matrix: include: - platform: macos-latest target: aarch64-apple-darwin os_key: darwin arch: aarch64 formats: app,dmg - platform: macos-latest target: x86_64-apple-darwin os_key: darwin arch: x86_64 formats: app,dmg macos_deployment_target: '10.14' - platform: ubuntu-22.04 target: '' os_key: linux arch: x86_64 formats: deb,appimage - platform: windows-latest target: '' os_key: windows arch: x86_64 formats: nsis runs-on: ${{ matrix.platform }} steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - name: (macOS) Pin deployment target if: matrix.macos_deployment_target != '' shell: bash run: echo "MACOSX_DEPLOYMENT_TARGET=${{ matrix.macos_deployment_target }}" >> "$GITHUB_ENV" - name: Setup Rust uses: ./.github/actions/setup-rust with: target: ${{ matrix.target }} save-cache: true restore-cache: true # Separate namespace: nightly's thin-LTO dependency artifacts have # different cargo fingerprints than the release workflow's fat-LTO ones, # so a shared cache would just thrash between the two. cache-namespace: nightly-cache - name: (Linux) Install system dependencies if: matrix.platform == 'ubuntu-22.04' run: | sudo apt-get update sudo apt-get install -y --no-install-recommends \ build-essential cmake pkg-config clang python3 \ libfontconfig-dev libfreetype-dev \ libgl1-mesa-dev libx11-dev libxcursor-dev libxi-dev libxrandr-dev \ libxkbcommon-dev libxkbcommon-x11-dev libwayland-dev libdbus-1-dev \ libfuse2 - name: Install cargo-packager uses: ./.github/actions/install-cargo-tool with: tool: cargo-packager version: 0.11.8 - name: Build release binary shell: bash run: | if [ -n "${{ matrix.target }}" ]; then cargo build --release --bin oneclient_app --target ${{ matrix.target }} else cargo build --release --bin oneclient_app fi - name: (macOS) Verify deployment target if: matrix.macos_deployment_target != '' shell: bash run: | set -euo pipefail bin="target/${{ matrix.target }}/release/oneclient_app" minos="$(vtool -show-build "$bin" | awk '/minos/ {print $2; exit}')" echo "linked minos=$minos, declared minimum=${{ matrix.macos_deployment_target }}" if [ "$(printf '%s\n%s\n' "$minos" "${{ matrix.macos_deployment_target }}" | sort -V | tail -n1)" != "${{ matrix.macos_deployment_target }}" ]; then echo "::error::Binary requires macOS $minos but the bundle advertises ${{ matrix.macos_deployment_target }}." exit 1 fi - name: (macOS) Compile app icon catalog if: startsWith(matrix.platform, 'macos') shell: bash run: | set -euo pipefail out="packages/oneclient_app/icons/generated" mkdir -p "$out" xcrun actool packages/oneclient_app/icons/OneClient.icon \ --compile "$out" \ --app-icon OneClient \ --output-partial-info-plist "$out/partial.plist" \ --platform macosx \ --minimum-deployment-target 26.0 \ --errors --warnings --notices rm -f "$out/OneClient.icns" test -f "$out/Assets.car" || { echo "::error::actool produced no Assets.car"; exit 1; } - name: (Windows) Stage app-local VC++ runtime if: matrix.platform == 'windows-latest' shell: bash run: python3 distribution/windows/bundle-crt.py - name: (Windows) Verify staged runtime is Microsoft-signed if: matrix.platform == 'windows-latest' shell: pwsh run: | $dlls = @(Get-ChildItem 'target/crt/*.dll') if ($dlls.Count -eq 0) { Write-Error 'no CRT DLLs staged'; exit 1 } foreach ($dll in $dlls) { $sig = Get-AuthenticodeSignature $dll.FullName Write-Host "$($dll.Name): $($sig.Status) $($sig.SignerCertificate.Subject)" if ($sig.Status -ne 'Valid') { Write-Error "$($dll.Name) signature is $($sig.Status), refusing to ship it" exit 1 } if ($sig.SignerCertificate.Subject -notmatch 'O=Microsoft Corporation') { Write-Error "$($dll.Name) is not signed by Microsoft" exit 1 } } - name: Package (unsigned) shell: bash env: CI: 'true' run: | ARGS="--release --formats ${{ matrix.formats }}" if [ -n "${{ matrix.target }}" ]; then ARGS="$ARGS --target ${{ matrix.target }}" fi cargo packager $ARGS --verbose - name: Resolve packager output dir id: outdir shell: bash run: | if [ -n "${{ matrix.target }}" ]; then echo "dir=target/${{ matrix.target }}/release" >> "$GITHUB_OUTPUT" else echo "dir=target/release" >> "$GITHUB_OUTPUT" fi - name: (Linux) Install cargo-generate-rpm if: matrix.platform == 'ubuntu-22.04' uses: ./.github/actions/install-cargo-tool with: tool: cargo-generate-rpm version: 0.21.0 - name: (Linux) Build RPM if: matrix.platform == 'ubuntu-22.04' shell: bash run: | set -euo pipefail cp target/release/.cargo-packager/deb/*/data/usr/share/applications/oneclient_app.desktop \ target/release/oneclient_app.desktop cargo generate-rpm -p packages/oneclient_app cp target/generate-rpm/*.rpm "${{ steps.outdir.outputs.dir }}/" - name: (Windows) Smoke-test installer layout if: matrix.platform == 'windows-latest' shell: pwsh run: | $ErrorActionPreference = 'Stop' $dest = 'C:\oneclient-smoke' Remove-Item -Recurse -Force $dest -ErrorAction SilentlyContinue $exe = Resolve-Path (Get-ChildItem '${{ steps.outdir.outputs.dir }}/*-setup.exe')[0].FullName # NSIS: /S is silent, /D= sets the install dir and must be the last arg. $p = Start-Process -FilePath $exe -ArgumentList '/S', "/D=$dest" -Wait -PassThru if ($p.ExitCode -ne 0) { Write-Error "installer exited $($p.ExitCode)"; exit 1 } Write-Host '--- installed layout ---' Get-ChildItem -Recurse $dest | ForEach-Object { Write-Host $_.FullName } if (-not (Test-Path "$dest\oneclient_app.exe")) { Write-Error "oneclient_app.exe is not at the root of $dest" exit 1 } foreach ($dll in @(Get-ChildItem 'target/crt/*.dll')) { if (-not (Test-Path (Join-Path $dest $dll.Name))) { Write-Error "$($dll.Name) did not land beside oneclient_app.exe - cargo-packager put resources elsewhere, so app-local CRT loading will not work" exit 1 } } Write-Host "OK: $($(Get-ChildItem 'target/crt/*.dll').Count) CRT DLLs sit beside oneclient_app.exe" # Upload debug info (dSYM / .dwp / .pdb produced by the release profile's # split-debuginfo) to Sentry so nightly crash reports get real file/function/ # line stack traces instead of raw addresses. Runs on every main push, so # each unique build's symbols land in Sentry (matched by debug-id). Skips # silently unless SENTRY_AUTH_TOKEN secret + SENTRY_ORG/SENTRY_PROJECT vars # are configured. - name: Upload debug symbols to Sentry shell: bash env: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_ORG: ${{ vars.SENTRY_ORG }} SENTRY_PROJECT: ${{ vars.SENTRY_PROJECT }} OUT_DIR: ${{ steps.outdir.outputs.dir }} run: | set -euo pipefail if [ -z "${SENTRY_AUTH_TOKEN:-}" ] || [ -z "${SENTRY_ORG:-}" ] || [ -z "${SENTRY_PROJECT:-}" ]; then echo "Sentry credentials not configured (need SENTRY_AUTH_TOKEN secret + SENTRY_ORG/SENTRY_PROJECT vars); skipping debug-symbol upload." exit 0 fi curl -sL https://sentry.io/get-cli/ | bash export PATH="/usr/local/bin:$PATH" SYMS="$(mktemp -d)" shopt -s nullglob for f in "$OUT_DIR"/oneclient_app "$OUT_DIR"/oneclient_app.exe \ "$OUT_DIR"/oneclient_app.pdb "$OUT_DIR"/*.dwp "$OUT_DIR"/*.dSYM; do [ -e "$f" ] || continue cp -R "$f" "$SYMS/" done sentry-cli debug-files upload --include-sources "$SYMS" - name: Collect installers id: collect shell: bash run: | set -euo pipefail shopt -s nullglob mkdir -p dist # Human-facing installers only; skip the .sig-paired updater bundles # (no updater manifest is produced for unsigned nightly builds). for f in "${{ steps.outdir.outputs.dir }}"/*.dmg \ "${{ steps.outdir.outputs.dir }}"/*.deb \ "${{ steps.outdir.outputs.dir }}"/*.rpm \ "${{ steps.outdir.outputs.dir }}"/*.AppImage \ "${{ steps.outdir.outputs.dir }}"/*-setup.exe; do [ -e "$f" ] || continue cp "$f" dist/ done if [ -z "$(ls -A dist)" ]; then echo "::error::No installers found in ${{ steps.outdir.outputs.dir }}" exit 1 fi ls -la dist - name: Upload unsigned installers uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: oneclient-nightly-${{ matrix.os_key }}-${{ matrix.arch }} path: dist/* if-no-files-found: error retention-days: 14