Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .github/release-notes/v1.0.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
The first full release of ProDHCPServer: a portable DHCP server for Windows 10/11, built for AV field technicians. Plug your laptop into a device, press **Start**, and see within seconds whether it got an IP address, with a plain-English reason and next step if it didn't. Press **Stop** and your laptop's network settings go back exactly as they were.

All three phases passed hands-on testing with real AV equipment in October 2026.

## Download

1. Download `ProDHCPServer-v1.0.0-win-x64.exe` below. It's a single file: nothing to install, no .NET runtime needed.
2. Double-click it. Windows asks for administrator rights, because changing adapter settings and serving DHCP need them.

> [!IMPORTANT]
> **The exe isn't code-signed yet.** The first time you run it, Windows SmartScreen says "Windows protected your PC": click **More info**, then **Run anyway**. On IT-managed work laptops, Microsoft Defender's attack surface reduction rules may block it outright. Run it on an unmanaged laptop, or ask IT to allow a fixed folder (for example `C:\Tools\ProDHCPServer\`).

To check the download wasn't corrupted, compare its SHA-256 with the `.sha256` file below. In PowerShell: `Get-FileHash .\ProDHCPServer-v1.0.0-win-x64.exe`

## What it does

**Core loop**
- Pick your Ethernet adapter; its settings are backed up before anything changes and restored on Stop, on exit, at shutdown, and after a crash.
- Eight-step status panel (adapter, link, static IP, DHCP server, DHCP traffic, lease, ping, WebUI), each green, amber or red with a reason and a suggested next step.
- Refuses to start if another DHCP server is already on the cable, so it won't fight a venue network.
- Device card with IP, MAC, manufacturer (offline lookup), and one-click **Open WebUI**.
- Warns about Wi-Fi or VPN on the same subnet, and about USB Ethernet adapters that have quietly stopped receiving (including old Realtek drivers).

**Visibility**
- Live ping graph for every device, with average, min, max and loss.
- Session log you can filter and export as text or CSV.
- Profiles for subnet, pool, lease time and DHCP options (gateway, DNS, NTP, options 43, 66, 150 and custom), with presets built from manufacturers' own documentation for Q-SYS, Biamp, Shure, Crestron, Extron, Lightware, Cisco and Poly gear and more.

**Discovery**
- **Found on the network** lists devices with a static IP, even on another subnet.
- **Reach it** adds a temporary address on the device's subnet so you can open its WebUI without stopping the server; it's removed on Stop like every other change.
- **Scan for static IPs** for quiet devices, covering your subnet and the common factory subnets.
- Read-only discovery over mDNS, SSDP/UPnP, PJLink, Art-Net, AES67 (SAP) and AMX, Q-SYS and Shure discovery.
- With [Npcap](https://npcap.com) installed, shows which switch, port and VLAN you're plugged into (LLDP/CDP).

## Known limits

- PJLink discovery hasn't been tried on a real projector yet.
- AV-over-IP multicast (Extron NAV, Dante video, SDVoE) on an unmanaged switch makes pings jump to hundreds of milliseconds. That's the network, not the app: use a managed switch with IGMP snooping.

Found a problem on site? [Open an issue](https://github.com/cookingwithsimon/ProDHCPServer/issues/new/choose) with the **Problem on site** form and include the session log.
84 changes: 84 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
name: Release

# Push a tag like v1.0.0 to build the exe and create a draft GitHub Release with it.
# The draft stays private until someone presses Publish on the Releases page.
on:
push:
tags: ['v*']

permissions:
contents: write # create the draft release and upload its files

jobs:
release:
runs-on: windows-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4

- uses: actions/setup-dotnet@v4
with:
global-json-file: global.json

- name: Read version from tag
id: version
shell: pwsh
run: |
$tag = "${{ github.ref_name }}"
if ($tag -notmatch '^v(\d+\.\d+\.\d+)$') { throw "Tag '$tag' must look like v1.2.3" }
"version=$($Matches[1])" >> $env:GITHUB_OUTPUT
if (-not (Test-Path ".github/release-notes/$tag.md")) { throw "Add .github/release-notes/$tag.md before tagging" }

- name: Restore
run: dotnet restore ProDHCPServer.sln

- name: Build
run: dotnet build ProDHCPServer.sln -c Debug --no-restore

- name: Core tests
run: dotnet test tests/ProDHCPServer.Core.Tests --no-build

- name: Windows integration tests (no adapter changes)
run: dotnet test tests/ProDHCPServer.Windows.Tests --no-build

- name: Publish single-file exe
run: dotnet publish src/ProDHCPServer.App -c Release -r win-x64 -p:Version=${{ steps.version.outputs.version }} -o publish

- name: Smoke test (launch in light and dark, close)
shell: pwsh
run: tools/smoke-test.ps1 -Exe publish/ProDHCPServer.exe -OutDir screenshots

- name: Name the exe and write its SHA-256 checksum
shell: pwsh
run: |
$name = "ProDHCPServer-${{ github.ref_name }}-win-x64.exe"
New-Item -ItemType Directory -Force -Path dist | Out-Null
Copy-Item publish/ProDHCPServer.exe "dist/$name"
$hash = (Get-FileHash "dist/$name" -Algorithm SHA256).Hash.ToLowerInvariant()
# Same format as sha256sum, so `sha256sum -c` works too.
Set-Content -NoNewline -Encoding ascii "dist/$name.sha256" "$hash $name`n"
Get-ChildItem dist

- name: Create or update the draft release
shell: pwsh
env:
GH_TOKEN: ${{ github.token }}
run: |
$tag = "${{ github.ref_name }}"
$files = (Get-ChildItem dist).FullName
gh release view $tag *> $null
if ($LASTEXITCODE -eq 0) {
gh release upload $tag @files --clobber
} else {
gh release create $tag @files --draft --verify-tag --title "ProDHCPServer $tag" --notes-file ".github/release-notes/$tag.md"
}
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }

- uses: actions/upload-artifact@v4
if: always()
with:
name: release-${{ github.ref_name }}
path: |
dist/
screenshots/
if-no-files-found: ignore
Loading