Add release build configuration and MSI installer

- Configure self-contained, single-file release build
- Add version and company metadata to project
- Fix Assembly.Location for single-file compatibility (use AppContext.BaseDirectory)
- Add Resources folder copy to publish output
- Create WiX installer project with Product.wxs
- Configure auto-start via HKCU Run registry key
- Add build-release.ps1 PowerShell script for automated builds
- MSI installer installs to %LOCALAPPDATA%\DangerousThings\NFC Actions
- Creates Start Menu shortcut
- Automatically starts application on user login

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-06 23:21:14 -08:00
parent 5b6a7f0fa5
commit 160d441f9a
5 changed files with 277 additions and 4 deletions

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" InitialTargets="EnsureWixToolsetInstalled" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>3.11</ProductVersion>
<ProjectGuid>f5e6a7b8-c9d0-4e1f-2a3b-4c5d6e7f8a9b</ProjectGuid>
<SchemaVersion>2.0</SchemaVersion>
<OutputName>NfcActions-Setup</OutputName>
<OutputType>Package</OutputType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<OutputPath>bin\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
<DefineConstants>Debug</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<OutputPath>bin\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Include="Product.wxs" />
</ItemGroup>
<ItemGroup>
<WixExtension Include="WixUIExtension">
<HintPath>$(WixExtDir)\WixUIExtension.dll</HintPath>
<Name>WixUIExtension</Name>
</WixExtension>
</ItemGroup>
<Import Project="$(WixTargetsPath)" Condition=" '$(WixTargetsPath)' != '' " />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets" Condition=" '$(WixTargetsPath)' == '' AND Exists('$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets') " />
<Target Name="EnsureWixToolsetInstalled" Condition=" '$(WixTargetsImported)' != 'true' ">
<Error Text="The WiX Toolset v3.11 (or newer) build tools must be installed to build this project. To download the WiX Toolset, see http://wixtoolset.org/releases/" />
</Target>
</Project>

101
Installer/Product.wxs Normal file
View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*"
Name="NFC Actions"
Language="1033"
Version="1.0.0.0"
Manufacturer="Dangerous Things"
UpgradeCode="A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D">
<Package InstallerVersion="200"
Compressed="yes"
InstallScope="perUser"
Description="NFC Actions - NFC card reader monitoring and automation"
Comments="Visit dangerousthings.com for more information" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate EmbedCab="yes" />
<!-- Suppress ICE validation warnings for per-user installation -->
<Property Id="DISABLEADVTSHORTCUTS" Value="1" />
<Property Id="MSIINSTALLPERUSER" Value="1" />
<Icon Id="ProductIcon" SourceFile="..\NfcActions\Resources\icon.ico" />
<Property Id="ARPPRODUCTICON" Value="ProductIcon" />
<Property Id="ARPHELPLINK" Value="https://dangerousthings.com" />
<Property Id="ARPURLINFOABOUT" Value="https://dangerousthings.com" />
<Feature Id="ProductFeature" Title="NFC Actions" Level="1">
<ComponentGroupRef Id="ProductComponents" />
<ComponentRef Id="ApplicationShortcut" />
<ComponentRef Id="AutoStartRegistry" />
</Feature>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="LocalAppDataFolder">
<Directory Id="CompanyFolder" Name="DangerousThings">
<Directory Id="INSTALLFOLDER" Name="NFC Actions" />
</Directory>
</Directory>
<Directory Id="ProgramMenuFolder">
<Directory Id="ApplicationProgramsFolder" Name="NFC Actions"/>
</Directory>
</Directory>
<DirectoryRef Id="ApplicationProgramsFolder">
<Component Id="ApplicationShortcut" Guid="B2C3D4E5-F6A7-4B6C-9D0E-1F2A3B4C5D6E">
<Shortcut Id="ApplicationStartMenuShortcut"
Name="NFC Actions"
Description="NFC card reader monitoring and automation"
Target="[INSTALLFOLDER]NfcActions.exe"
WorkingDirectory="INSTALLFOLDER"
Icon="ProductIcon" />
<RemoveFolder Id="CleanUpShortCut" Directory="ApplicationProgramsFolder" On="uninstall"/>
<RegistryValue Root="HKCU"
Key="Software\DangerousThings\NfcActions"
Name="installed"
Type="integer"
Value="1"
KeyPath="yes"/>
</Component>
</DirectoryRef>
<DirectoryRef Id="INSTALLFOLDER">
<Component Id="AutoStartRegistry" Guid="C3D4E5F6-A7B8-4C7D-0E1F-2A3B4C5D6E7F">
<RegistryValue Root="HKCU"
Key="Software\Microsoft\Windows\CurrentVersion\Run"
Name="NFC Actions"
Type="string"
Value="&quot;[INSTALLFOLDER]NfcActions.exe&quot;"
KeyPath="yes"/>
</Component>
</DirectoryRef>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component Id="MainExecutable" Guid="D4E5F6A7-B8C9-4D8E-1F2A-3B4C5D6E7F8A">
<File Id="NfcActionsExe"
Source="..\NfcActions\bin\Release\net7.0-windows\win-x64\publish\NfcActions.exe" />
<RegistryValue Root="HKCU"
Key="Software\DangerousThings\NfcActions"
Name="MainExe"
Type="integer"
Value="1"
KeyPath="yes"/>
<RemoveFolder Id="RemoveINSTALLFOLDER" Directory="INSTALLFOLDER" On="uninstall" />
<RemoveFolder Id="RemoveCompanyFolder" Directory="CompanyFolder" On="uninstall" />
</Component>
<Component Id="ResourcesFolder" Guid="E5F6A7B8-C9D0-4E9F-2A3B-4C5D6E7F8A9B">
<File Id="IconIco" Source="..\NfcActions\bin\Release\net7.0-windows\win-x64\publish\Resources\icon.ico" />
<RegistryValue Root="HKCU"
Key="Software\DangerousThings\NfcActions"
Name="Resources"
Type="integer"
Value="1"
KeyPath="yes"/>
</Component>
</ComponentGroup>
</Product>
</Wix>

View File

@@ -7,6 +7,21 @@
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
<ApplicationIcon>Resources\icon.ico</ApplicationIcon>
<Version>1.0.0</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
<Company>Dangerous Things</Company>
<Product>NFC Actions</Product>
<Description>NFC card reader monitoring and action automation</Description>
<Copyright>Copyright © 2025 Dangerous Things</Copyright>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<PublishReadyToRun>true</PublishReadyToRun>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
</PropertyGroup>
<ItemGroup>
@@ -22,7 +37,18 @@
</ItemGroup>
<ItemGroup>
<None Include="Resources\icon.ico" CopyToOutputDirectory="PreserveNewest" />
<None Include="Resources\**\*">
<Link>Resources\%(RecursiveDir)%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
</ItemGroup>
<Target Name="CopyResourcesOnPublish" AfterTargets="Publish">
<ItemGroup>
<ResourceFiles Include="$(ProjectDir)Resources\**\*" />
</ItemGroup>
<Copy SourceFiles="@(ResourceFiles)" DestinationFolder="$(PublishDir)Resources\%(RecursiveDir)" SkipUnchangedFiles="true" />
</Target>
</Project>

View File

@@ -21,9 +21,8 @@ public class LogService
{
_syncContext = SynchronizationContext.Current;
// Create log file in the same directory as the executable
var exePath = Assembly.GetExecutingAssembly().Location;
var exeDir = Path.GetDirectoryName(exePath) ?? Environment.CurrentDirectory;
// Create log file in the app directory (works for single-file publish)
var exeDir = AppContext.BaseDirectory;
var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
_logFilePath = Path.Combine(exeDir, $"nfc-actions-debug-{timestamp}.log");

112
build-release.ps1 Normal file
View File

@@ -0,0 +1,112 @@
# NFC Actions Release Build Script
# This script builds the release version and optionally creates an installer
param(
[switch]$SkipBuild = $false,
[switch]$BuildInstaller = $true
)
$ErrorActionPreference = "Stop"
$scriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
$projectPath = Join-Path $scriptPath "NfcActions\NfcActions.csproj"
$publishPath = Join-Path $scriptPath "NfcActions\bin\Release\net7.0-windows\win-x64\publish"
$installerPath = Join-Path $scriptPath "Installer"
Write-Host "=== NFC Actions Release Build ===" -ForegroundColor Cyan
Write-Host ""
# Step 1: Build and publish the application
if (-not $SkipBuild) {
Write-Host "[1/4] Cleaning previous builds..." -ForegroundColor Yellow
dotnet clean $projectPath -c Release -v quiet
Write-Host "[2/4] Building release (self-contained, single-file)..." -ForegroundColor Yellow
dotnet publish $projectPath -c Release -r win-x64 --self-contained true /p:PublishSingleFile=true /p:IncludeNativeLibrariesForSelfExtract=true /p:PublishReadyToRun=true
if ($LASTEXITCODE -ne 0) {
Write-Host "Build failed!" -ForegroundColor Red
exit 1
}
Write-Host "Build completed successfully!" -ForegroundColor Green
Write-Host "Output: $publishPath" -ForegroundColor Gray
Write-Host ""
}
# Step 2: Check if WiX is available
if ($BuildInstaller) {
Write-Host "[3/4] Checking for WiX Toolset..." -ForegroundColor Yellow
$wixInstalled = $false
$candle = Get-Command candle.exe -ErrorAction SilentlyContinue
$light = Get-Command light.exe -ErrorAction SilentlyContinue
if ($candle -and $light) {
$wixInstalled = $true
Write-Host "WiX Toolset found!" -ForegroundColor Green
} else {
Write-Host "WiX Toolset not found in PATH" -ForegroundColor Yellow
# Check common installation paths
$wixPaths = @(
"C:\Program Files (x86)\WiX Toolset v3.11\bin",
"C:\Program Files (x86)\WiX Toolset v3.14\bin",
"C:\Program Files\WiX Toolset v3.11\bin",
"C:\Program Files\WiX Toolset v3.14\bin"
)
foreach ($path in $wixPaths) {
if (Test-Path (Join-Path $path "candle.exe")) {
$env:Path += ";$path"
$wixInstalled = $true
Write-Host "Found WiX at: $path" -ForegroundColor Green
break
}
}
}
if ($wixInstalled) {
Write-Host "[4/4] Building MSI installer..." -ForegroundColor Yellow
Push-Location $installerPath
# Run candle (compile)
& candle.exe Product.wxs -out obj\Product.wixobj
if ($LASTEXITCODE -ne 0) {
Pop-Location
Write-Host "Candle (WiX compile) failed!" -ForegroundColor Red
exit 1
}
# Run light (link)
& light.exe obj\Product.wixobj -out bin\NfcActions-Setup.msi -ext WixUIExtension
if ($LASTEXITCODE -ne 0) {
Pop-Location
Write-Host "Light (WiX link) failed!" -ForegroundColor Red
exit 1
}
Pop-Location
$msiPath = Join-Path $installerPath "bin\NfcActions-Setup.msi"
Write-Host ""
Write-Host "=== Build Complete ===" -ForegroundColor Green
Write-Host "MSI Installer: $msiPath" -ForegroundColor Cyan
} else {
Write-Host ""
Write-Host "WiX Toolset not found. Skipping MSI creation." -ForegroundColor Yellow
Write-Host "To build the MSI installer, install WiX Toolset from:" -ForegroundColor Yellow
Write-Host " https://github.com/wixtoolset/wix3/releases" -ForegroundColor Gray
Write-Host ""
Write-Host "=== Build Complete ===" -ForegroundColor Green
Write-Host "Executable: $publishPath\NfcActions.exe" -ForegroundColor Cyan
}
} else {
Write-Host "[3/4] Skipping installer build" -ForegroundColor Yellow
Write-Host ""
Write-Host "=== Build Complete ===" -ForegroundColor Green
Write-Host "Executable: $publishPath\NfcActions.exe" -ForegroundColor Cyan
}
Write-Host ""