This directory contains the WiX (v4/v5) sources that package FileCommand
into a single bootstrapper executable, FileCommandSetup.exe, plus the
winget manifest template and Chocolatey package used to distribute it.
PackagePerUser.wxs— the per-user MSI (Scope="perUser"): installs with no elevation, user PATH integration, Start Menu shortcut, versioned upgrades. This is the default scope.PackagePerMachine.wxs— the per-machine MSI (Scope="perMachine"): elevated install to Program Files, machine PATH, its own UpgradeCode (scope switches are uninstall-then-reinstall, never upgrades).Bundle.wxs— the Burn bundle that embeds both MSIs behind the WiX Standard Bootstrapper Application (WixStdBA) UI, producingFileCommandSetup.exe; exactly one MSI is planned per run, selected by theInstallScopevariable. (Two single-scope MSIs replaced the original dual-scope design after testing showed WiX v5's Burn always elevates a dual-scope package — see design.md D1.)License.rtf— license text shown by the bootstrapper UI (FileCommand is dual-licensed MIT / Apache-2.0; seeLICENSE-MIT/LICENSE-APACHEat the repo root).theme/— the bundle's custom WixStdBA theme: a copy of the built-inrtfLicensetheme (RtfTheme.xml/RtfTheme.wxl) extended with a scope-coexistence warning on the install page (see Scope semantics below).build.ps1— builds the release binary and both WiX artifacts.winget/— the winget package manifest template.
See openspec/changes/wix-installer/design.md for the rationale behind
these choices (dual-scope packaging, install paths, PATH handling, the
Burn scope switch, and versioning).
-
Rust toolchain (
cargo) — https://rustup.rs -
.NET SDK — https://dotnet.microsoft.com/download (required to run the WiX CLI, which is a .NET global tool)
-
WiX Toolset v4/v5 CLI:
dotnet tool install --global wix wix extension add WixToolset.BootstrapperApplications.wixext/5.0.2 wix extension add WixToolset.Util.wixext/5.0.2
Pin the extensions to
5.0.2explicitly —wix extension addwithout a version resolves the latest release (currently 7.0.0), which is not compatible with the WiX v5 CLI and fails the bundle build withWIX0144: The extension ... could not be found. The bundle build uses both extensions: BootstrapperApplications for the WixStdBA UI, and Util for theutil:ProductSearchsearches that detect the other install scope (see Scope semantics).The custom theme under
theme/is copied from the built-inrtfLicensetheme embedded inWixToolset.BootstrapperApplications.wixext5.0.2. If either extension pin is bumped, re-derivetheme/RtfTheme.xmlandtheme/RtfTheme.wxlfrom the new extension version and re-apply the warning-label addition (each file's header comment describes the change), then re-test the bundle UI.
build.ps1 checks for dotnet, wix, and cargo on PATH up front and
fails fast with an install hint if any are missing.
From the repo root, or from this directory:
.\installer\build.ps1This runs cargo build --release, reads the current version from the
workspace Cargo.toml ([workspace.package].version), stamps it into
both MSIs' ProductVersion and the bundle's Version, and produces:
installer\out\PackagePerUser.msiinstaller\out\PackagePerMachine.msiinstaller\out\FileCommandSetup.exe
Pass -SkipCargoBuild to repackage the existing
target\release\filecommand.exe without rebuilding Rust — useful while
iterating on the WiX authoring itself.
These are unsigned development builds. Unsigned executables will trip SmartScreen on machines that haven't seen them before; see Production signing below.
FileCommand installs to BigHatGroup\FileCommand under one of two roots,
selected by install scope:
| Scope | Path | Elevation | PATH scope |
|---|---|---|---|
| Per-user (default) | %LocalAppData%\Programs\BigHatGroup\FileCommand |
None | User PATH (HKCU) |
| Per-machine | %ProgramFiles%\BigHatGroup\FileCommand |
Elevates once (UAC) | Machine PATH (HKLM) |
Per-user is the default for both the interactive bootstrapper UI and silent installs — the first-run experience never prompts for credentials unless per-machine is explicitly requested.
The installer itself ships no configuration files: at runtime FileCommand
keeps its per-user settings (config.toml, history.json,
usermenu.toml) under %APPDATA%\FileCommand by default, regardless of
install scope. See
Uninstalling and removing settings
for the full profile-directory resolution order and how to remove those
files.
- Interactive:
FileCommandSetup.exe— installs per-user by default; the WixStdBA UI does not currently expose a scope picker (SuppressOptionsUI="yes"). To get a per-machine interactive install, launch elevated with the scope override below. - Silent, per-user (default):
FileCommandSetup.exe /quiet - Silent, per-machine:
FileCommandSetup.exe /quiet InstallScope=perMachine(must be run elevated; this is the switch winget's--scope machinemaps to — seewinget/) - Silent uninstall:
FileCommandSetup.exe /uninstall /quiet
Burn's standard switches (/quiet, /passive, /norestart, /uninstall,
/log <path>) all work as usual; only the InstallScope bundle variable
is FileCommand-specific.
Installing a newer bundle upgrades in place at the same scope the
existing install used (matched by UpgradeCode; see design.md decision
D5). Switching scope — e.g. going from a per-user install to a
per-machine one — is not an upgrade path: the two scopes are separate
MSIs with different UpgradeCodes, so installing at the other scope will
coexist with (not replace) the first install. To change scope:
uninstall the existing install, then install fresh at the new scope.
When both scopes are installed, both install directories are on PATH
— and because Windows builds a process's PATH as the machine PATH
followed by the user PATH, typing filecommand always starts the
per-machine copy (%ProgramFiles%\BigHatGroup\FileCommand), whichever
scope was installed last. A per-user install or upgrade made after a
per-machine one is therefore shadowed: the user upgrades, and the old
per-machine version keeps starting.
The bundle detects this at install time: before planning, it checks by
UpgradeCode whether the other scope's MSI is already installed, and
on an interactive run the install page warns which copy will run. The
warning never blocks — Install proceeds as usual. Quiet and passive runs
(/quiet, /passive, winget, Chocolatey) are never prompted; the
detection result is simply recorded in the Burn log (run with
/log <path> to see it). Mixing package managers is the most common way
to end up with both scopes: Chocolatey installs per-machine by default
(see chocolatey/), while winget's default maps to a per-user install
(see winget/).
Windows Installer updates the registry PATH value and broadcasts
WM_SETTINGCHANGE on install/uninstall, which most well-behaved
applications (Explorer, new shell instances) pick up automatically.
Shells already running at the time of install will not see the updated
PATH — this is standard Windows behavior, not a bug in this
installer. Open a new terminal window to get filecommand on PATH
after installing.
Uninstalling FileCommand — from Apps & features, or with
FileCommandSetup.exe /uninstall (add /quiet for a silent uninstall) —
removes the program files, the Start Menu shortcut, and the PATH entry
the install added, but keeps your settings, so a later reinstall
picks them up. The installer never deletes user data; to remove
FileCommand completely, delete the settings directory by hand as
described below.
FileCommand resolves its profile directory once at startup, in this order:
--config-dir <path>— whatever directory was passed on the command line;- a file named
portablebesidefilecommand.exe— portable mode: the settings live in the executable's own directory (the install directory); - otherwise
%APPDATA%\FileCommand(the default on Windows).
The default profile directory holds config.toml, history.json, and
usermenu.toml. Because %APPDATA% is per-account, a per-machine
install leaves one copy under %APPDATA%\FileCommand for each Windows
account that ran FileCommand — uninstalling from one account cannot
(and does not) touch the others.
Run as each affected user:
# PowerShell
Remove-Item -Recurse -Force "$env:APPDATA\FileCommand":: cmd.exe
rmdir /s /q "%APPDATA%\FileCommand"If a portable file sits beside filecommand.exe, the settings files
live in the install directory instead of %APPDATA%. Uninstall removes
only what the installer put there, so it leaves the settings files, the
portable marker, and therefore the folder itself. Delete the folder by
hand:
- per-user:
%LOCALAPPDATA%\Programs\BigHatGroup\FileCommand - per-machine:
%ProgramFiles%\BigHatGroup\FileCommand(requires elevation)
If FileCommand was launched with --config-dir <path>, the settings live
wherever that pointed. The installer never touches that location; delete
it by hand if you want it gone.
Plain build.ps1 produces unsigned development builds. Pass -Sign
to sign everything with Azure Trusted Signing (Big Hat Group Inc.'s
BHGPublic/private-mcp certificate profile, from the
CodeSigning tooling):
.\installer\build.ps1 -SignThis signs, in order: filecommand.exe (before packaging), both MSIs
(before the bundle build, so the bundle embeds already-signed MSIs), then
the Burn bundle itself via the two-pass sequence — wix burn detach to
extract the stub engine, sign it, wix burn reattach, then sign the
final FileCommandSetup.exe. (wix burn detach/reattach are the WiX
v4/v5 replacements for the old standalone insignia tool; see
https://docs.firegiant.com/wix/tools/signing/ for background.) Every
signed file is verified with signtool verify /pa immediately after
signing, and the whole build fails fast if any signature doesn't
validate.
-Sign requires, on the machine running the build:
- An authenticated
az login/Connect-AzAccountsession with the Code Signing Certificate Profile Signer role on theBHGPublic/private-mcpprofile. - A local checkout of the CodeSigning repo, for
Azure.CodeSigning.Dlib.dlland the Trusted Signing metadata JSON. Defaults to aCodeSigningcheckout next to this repo (for exampleC:\Temp\tsscat\CodeSigningbesideC:\Temp\tsscat\FileCommand); override with-CodeSigningDliband-CodeSigningMetadataif it lives elsewhere. - A Windows SDK signtool (10.0.26100.0+ recommended); auto-detected, or
pass
-SignToolPathexplicitly.
Unsigned builds remain fine for local development and testing — -Sign
is opt-in, not the default, since most contributors won't have the
signing certificate's role assignment.
See winget/ for the manifest template and its own notes. Submitting to
winget-pkgs is a release-time step outside this repository.
See chocolatey/ for the package template and its own notes. The package
wraps the same FileCommandSetup.exe (downloaded at install time, checksum
enforced), defaulting to a per-machine install per Chocolatey convention.
Pushing to the Chocolatey community repository is a release-time step
outside this repository.