diff --git a/.agents/skills/build-agent-msi/SKILL.md b/.agents/skills/build-agent-msi/SKILL.md new file mode 100644 index 000000000..75af686fd --- /dev/null +++ b/.agents/skills/build-agent-msi/SKILL.md @@ -0,0 +1,132 @@ +--- +name: build-agent-msi +description: Build the Devolutions Agent Windows MSI installer locally. Use when asked to build, compile, or rebuild the agent MSI. +--- + +# Build Devolutions Agent MSI Locally + +## Required Tools + +- **MSBuild** — VS 2022: `C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe` +- **MakeAppx.exe** — Windows SDK: `C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\MakeAppx.exe` +- **Rust/cargo** + +## Important Notes + +- **Static CRT is required.** Local builds do NOT link the CRT statically by default, but CI does. + Without `+crt-static`, the binary fails at runtime with exit code `-1073741515` (`0xC0000135`, DLL not found) + on any machine that doesn't have the MSVC redistributable installed — including fresh VMs. + Always set `$Env:RUSTFLAGS = "-C target-feature=+crt-static"` before `cargo build`. + +- **`devolutions-agent-updater` is NOT a separate package.** It is a `[[bin]]` target inside the + `devolutions-agent` package (see `devolutions-agent/Cargo.toml`). Building `-p devolutions-agent` + produces both `devolutions-agent.exe` and `devolutions-agent-updater.exe`. Do NOT use + `-p devolutions-agent-updater` — it will fail with "package ID specification did not match any packages". + +- **PEDM shell extension MSIX only builds in debug profile.** The MakeAppx step is not wired into the + release build. Always point `DAGENT_PEDM_SHELL_EXT_MSIX` at `target\debug\DevolutionsPedmShellExt.msix` + even when building a release MSI. + +- **MSI version encoding.** The MSI product version must have a major component < 256, so the year prefix + `20` must be stripped: `2026.1.0` → `26.1.0`. The registry decoder adds 2000 back. + +- **MSBuild output filename.** MSBuild writes `Release\DevolutionsAgent.msi`. Rename it to the versioned + form (`DevolutionsAgent-x86_64-.msi`) after the build if needed for distribution. + +## Step 1 — Build Rust Binaries + +```powershell +cd D:\devolutions-gateway +$Env:PATH += ";C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64" # needed for PEDM MSIX +$Env:RUSTFLAGS = "-C target-feature=+crt-static" # required — matches CI +cargo build --release -p devolutions-agent -p devolutions-session +cargo build -p devolutions-pedm-shell-ext # debug only — MSIX step not wired into release profile +# devolutions-agent-updater.exe is produced automatically alongside devolutions-agent.exe +``` + +## Step 2 — Download tun2socks and wintun + +```powershell +cd D:\devolutions-gateway +pwsh ci/download-tun2socks.ps1 +# Produces: tun2socks.exe and wintun.dll in the repo root +``` + +## Step 3 — Build the .NET DesktopAgent + +```powershell +cd D:\devolutions-gateway\dotnet\DesktopAgent +dotnet build +# Output: bin\Debug\net48\DevolutionsDesktopAgent.exe +``` + +## Step 4 — Build the MSI + +```powershell +cd D:\devolutions-gateway\package\AgentWindowsManaged + +$base = "D:\devolutions-gateway" +$Env:DAGENT_EXECUTABLE = "$base\target\release\devolutions-agent.exe" +$Env:DAGENT_UPDATER_EXECUTABLE = "$base\target\release\devolutions-agent-updater.exe" +$Env:DAGENT_PEDM_SHELL_EXT_DLL = "$base\target\release\devolutions_pedm_shell_ext.dll" +$Env:DAGENT_PEDM_SHELL_EXT_MSIX = "$base\target\debug\DevolutionsPedmShellExt.msix" # always debug +$Env:DAGENT_SESSION_EXECUTABLE = "$base\target\release\devolutions-session.exe" +$Env:DAGENT_TUN2SOCKS_EXE = "$base\tun2socks.exe" +$Env:DAGENT_WINTUN_DLL = "$base\wintun.dll" +$Env:DAGENT_DESKTOP_AGENT_PATH = "$base\dotnet\DesktopAgent\bin\Debug\net48" +$version = (Get-Content "$base\VERSION" -Raw).Trim() +if ($version.StartsWith("20")) { $version = $version.Substring(2) } # strip century: 2026.1.0 → 26.1.0 +$Env:DAGENT_VERSION = $version +$Env:DAGENT_PLATFORM = "x64" + +& "C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe" DevolutionsAgent.sln /t:clean,restore,build /p:Configuration=Release /verbosity:minimal +``` + +**Output:** `package/AgentWindowsManaged/Release/DevolutionsAgent.msi` + +Optionally rename to a versioned filename and compute the SHA-256 (needed for `productinfo.json`): + +```powershell +$msi = "D:\devolutions-gateway\package\AgentWindowsManaged\Release\DevolutionsAgent.msi" +$versioned = "D:\devolutions-gateway\package\AgentWindowsManaged\Release\DevolutionsAgent-x86_64-20$($Env:DAGENT_VERSION).0.msi" +Copy-Item $msi $versioned -Force +$hash = (Get-FileHash $versioned -Algorithm SHA256).Hash +$hash | Set-Content "$([System.IO.Path]::ChangeExtension($versioned, 'sha'))" +``` + +## Installing the MSI + +**Always install with administrator rights.** Double-clicking the MSI from Explorer runs the installer +under an impersonated (non-elevated) token, which causes `Error code 5` or silent failures in custom +actions that require elevation. + +Install from an **already-elevated** PowerShell prompt: + +```powershell +# Option 1 — msiexec with log (recommended for debugging) +msiexec /i "C:\path\to\DevolutionsAgent-x86_64-2026.1.0.0.msi" /l*v "C:\path\to\log.log" + +# Option 2 — silent install +msiexec /i "C:\path\to\DevolutionsAgent-x86_64-2026.1.0.0.msi" /quiet +``` + +## Common Build Failures + +### Missing `WixSharp.wix.bin` NuGet Package + +```powershell +cd D:\devolutions-gateway\package\AgentWindowsManaged +dotnet add package WixSharp.wix.bin --prerelease +# Must match the WixSharp version (3.14.1) +``` + +### MakeAppx Not Found During PEDM Shell Ext Build + +```powershell +$Env:PATH += ";C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64" +``` + +### `0xC0000135` / Exit Code `-1073741515` at Runtime + +The binary was built without static CRT. Rebuild with `$Env:RUSTFLAGS = "-C target-feature=+crt-static"` +(see Step 1). This matches the CI build and eliminates the dependency on the MSVC redistributable. diff --git a/.agents/skills/build-gateway-msi/SKILL.md b/.agents/skills/build-gateway-msi/SKILL.md new file mode 100644 index 000000000..9392df057 --- /dev/null +++ b/.agents/skills/build-gateway-msi/SKILL.md @@ -0,0 +1,231 @@ +--- +name: build-gateway-msi +description: Build the Devolutions Gateway Windows MSI installer locally. Use when asked to build, compile, or rebuild the gateway MSI. +--- + +# Build Devolutions Gateway MSI Locally + +## Required Tools + +- **MSBuild** — VS 2022: `C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe` +- **Rust/cargo** +- **pnpm** — for building the web app +- **PowerShell 7+** (`pwsh`) +- **dotnet** — for the PowerShell module build + +## Important Notes + +- **Static CRT is required for CI-matching builds.** Without `+crt-static`, the binary fails at runtime + with exit code `-1073741515` (`0xC0000135`, DLL not found) on machines without the MSVC redistributable. + Always set `$Env:RUSTFLAGS = "-C target-feature=+crt-static"` before `cargo build --release`. + For a local debug build this is optional, but release builds must use it. + +- **Version stripping.** The MSI product version must have a major component < 256, so the century + prefix `20` must be stripped: `2026.1.0` → `26.1.0`. This matches what `ci/Build/Build.psm1` does + via `$version.Substring(2)`. + +- **MSBuild output filename.** MSBuild writes `Release\DevolutionsGateway.msi`. Rename it to the + versioned form if needed for distribution. + +- **Web app build is slow** (~5-10 min). Skip it on repeated builds by reusing the existing + `webapp\dist\` output; only rebuild if webapp source changed. + +- **`download-cadeau.ps1` runs from `ci/`** and always writes to `../native-libs/`, which resolves to + the repo root's `native-libs\` directory. The file you need is `native-libs\xmf.dll`. + +## Step 1 — Build Rust Binary + +```powershell +cd D:\devolutions-gateway +$Env:RUSTFLAGS = "-C target-feature=+crt-static" # required for CI-matching release build +cargo build --release -p devolutions-gateway +# Output: target\release\devolutions-gateway.exe +``` + +For a quick local debug build (no static CRT required, faster): + +```powershell +cd D:\devolutions-gateway +cargo build -p devolutions-gateway +# Output: target\debug\devolutions-gateway.exe +``` + +## Step 2 — Download Cadeau (xmf.dll) + +```powershell +cd D:\devolutions-gateway +pwsh ci/download-cadeau.ps1 -Platform win -Architecture x64 +# Output: native-libs\xmf.dll +``` + +## Step 3 — Build PowerShell Module + +```powershell +cd D:\devolutions-gateway +pwsh powershell/build.ps1 +# Output: powershell\package\DevolutionsGateway\ +``` + +## Step 4 — Build Web App + +`@devolutions/*` packages are hosted on a private JFrog Artifactory registry. +Before running `pnpm install` for the first time, configure authentication: + +1. Log in: `npm login --registry=https://devolutions.jfrog.io/devolutions/api/npm/npm/` +2. Add these lines to `%USERPROFILE%\.npmrc` (they won't be added by `npm login` automatically): + ``` + @devolutions:registry=https://devolutions.jfrog.io/devolutions/api/npm/npm/ + registry=https://registry.npmjs.org + //devolutions.jfrog.io/artifactory/api/npm/npm/:_authToken= + ``` + The third line is needed because the lockfile contains some package tarballs + at `/artifactory/` paths (different from `/devolutions/`), so a second token entry is required. + +Then build: + +```powershell +cd D:\devolutions-gateway\webapp +pnpm install + +# Build in dependency order: shadow-player → multi-video-player → apps +pnpm --filter '@devolutions/shadow-player' build # packages/shadow-player +pnpm --filter '@devolutions/multi-video-player' build # packages/multi-video-player (depends on shadow-player) +pnpm --filter './apps/gateway-ui' build +pnpm --filter './apps/recording-player' build # depends on multi-video-player and shadow-player +# Outputs: +# webapp\dist\gateway-ui\ +# webapp\dist\recording-player\ +``` + +> **Note:** `pnpm build:libs` and `pnpm build:apps` may report "No projects matched" depending +> on pnpm version/workspace state. Use the explicit `--filter` commands above instead. + +## Step 5 — Build the MSI + +```powershell +cd D:\devolutions-gateway\package\WindowsManaged + +$msbuild = "C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe" +$base = "D:\devolutions-gateway" + +# Point at release binary (or replace with target\debug\devolutions-gateway.exe for debug build) +$Env:DGATEWAY_EXECUTABLE = "$base\target\release\devolutions-gateway.exe" +$Env:DGATEWAY_LIB_XMF_PATH = "$base\native-libs\xmf.dll" +$Env:DGATEWAY_PSMODULE_PATH = "$base\powershell\package\DevolutionsGateway" +$Env:DGATEWAY_WEBCLIENT_PATH = "$base\webapp\dist\gateway-ui" +$Env:DGATEWAY_WEBPLAYER_PATH = "$base\webapp\dist\recording-player" +$version = (Get-Content "$base\VERSION" -Raw).Trim() +if ($version.StartsWith("20")) { $version = $version.Substring(2) } # strip century: 2026.1.0 → 26.1.0 +$Env:DGATEWAY_VERSION = $version + +& $msbuild DevolutionsGateway.sln /t:clean,restore,build /p:Configuration=Release /verbosity:minimal +``` + +**Output:** `package/WindowsManaged/Release/DevolutionsGateway.msi` + +Optionally rename to a versioned filename and compute the SHA-256: + +```powershell +$msi = "D:\devolutions-gateway\package\WindowsManaged\Release\DevolutionsGateway.msi" +$versioned = "D:\devolutions-gateway\package\WindowsManaged\Release\DevolutionsGateway-x86_64-20$($Env:DGATEWAY_VERSION).0.msi" +Copy-Item $msi $versioned -Force +$hash = (Get-FileHash $versioned -Algorithm SHA256).Hash +$hash | Set-Content "$([System.IO.Path]::ChangeExtension($versioned, 'sha'))" +``` + +## Alternative — Use the Packaging Script + +The `ci/package-gateway-windows.ps1` script sets env vars and runs MSBuild for you: + +```powershell +$base = "D:\devolutions-gateway" +New-Item -ItemType Directory -Force -Path "$base\output\msi" | Out-Null + +pwsh "$base\ci\package-gateway-windows.ps1" ` + -Exe "$base\target\release\devolutions-gateway.exe" ` + -LibxmfFile "$base\native-libs\xmf.dll" ` + -PsModuleDir "$base\powershell\package\DevolutionsGateway" ` + -WebClientDir "$base\webapp\dist\gateway-ui" ` + -WebPlayerDir "$base\webapp\dist\recording-player" ` + -OutputDir "$base\output\msi" +# Copies MSI to output\msi\DevolutionsGateway.msi +``` + +Note: the script requires `MSBuild.exe` to be on `$Env:PATH`. If it isn't, prepend it: + +```powershell +$Env:PATH = "C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin;$Env:PATH" +``` + +## Installing the MSI + +**Always install with administrator rights.** Double-clicking from Explorer runs the installer under an +impersonated (non-elevated) token, which causes `Error code 5` or silent failures in custom actions. + +Install from an **already-elevated** PowerShell prompt: + +```powershell +# Option 1 — msiexec with log (recommended for debugging) +msiexec /i "C:\path\to\DevolutionsGateway.msi" /l*v "C:\path\to\log.log" + +# Option 2 — silent install +msiexec /i "C:\path\to\DevolutionsGateway.msi" /quiet +``` + +## Common Build Failures + +### Missing `WixSharp.wix.bin` NuGet Package + +``` +error MSB4018: The "MSBuild" task failed unexpectedly. +``` + +```powershell +cd D:\devolutions-gateway\package\WindowsManaged +dotnet add package WixSharp.wix.bin --prerelease +``` + +### `0xC0000135` / Exit Code `-1073741515` at Runtime + +The binary was built without static CRT. Rebuild with `$Env:RUSTFLAGS = "-C target-feature=+crt-static"` +(see Step 1). This matches CI and eliminates the dependency on the MSVC redistributable. + +### `DGATEWAY_EXECUTABLE` Not Found + +Program.cs checks that the file exists at MSBuild time. Ensure `cargo build` completed successfully +and the path in `$Env:DGATEWAY_EXECUTABLE` is correct (debug vs. release). + +### `DGATEWAY_PSMODULE_PATH` Not Found / Empty + +Run `pwsh powershell/build.ps1` first. The output directory is `powershell\package\DevolutionsGateway`. +If the build fails due to a missing NuGet source, the script adds `api.nuget.org` automatically; ensure +internet access or restore from cache. + +### `DGATEWAY_WEBCLIENT_PATH` / `DGATEWAY_WEBPLAYER_PATH` Not Found + +Run `pnpm install && pnpm build:libs && pnpm build:apps` in `webapp/`. Both `gateway-ui` and +`recording-player` must exist under `webapp\dist\` before the MSI build starts. + +### `pnpm install` Fails with `ERR_PNPM_FETCH_404` for `@devolutions/icons` + +`@devolutions/icons` is a private package not available on the public npm registry. It requires +access to a private npm registry (JFrog Artifactory). Fix: + +1. Log in: `npm login --registry=https://devolutions.jfrog.io/devolutions/api/npm/npm/` +2. Append to `%USERPROFILE%\.npmrc`: + ``` + @devolutions:registry=https://devolutions.jfrog.io/devolutions/api/npm/npm/ + registry=https://registry.npmjs.org + //devolutions.jfrog.io/artifactory/api/npm/npm/:_authToken= + ``` + The `/artifactory/` token entry is needed because some packages in the lockfile resolve + to that URL path rather than `/devolutions/`. + +### `pnpm build:libs` / `pnpm build:apps` — "No projects matched" + +Use explicit `--filter` calls instead (see Step 4). The glob filters in `package.json` scripts +may not resolve depending on pnpm version. + +The workspace library packages (`shadow-player`, `multi-video-player`) are in `packages/` and +must be built before the apps that consume them. Build order: `shadow-player` → `multi-video-player` +→ `gateway-ui` and `recording-player`.