Add release workflow, self-contained publish profiles, and version exposure - #16
Merged
Merged
Conversation
…posure Adds the pieces a Vortex extension's tool-acquisition logic needs to fetch a real, versioned build of this fork: self-contained single-file publish profiles for all three host/RID combinations, a --version CLI flag and MCP ServerInfo.Version on both hosts (backed by a new shared Core helper, VersionInfo.GetVersion), and a tag-triggered .github/workflows/release.yml that builds, tests, and publishes all three combinations and attaches them as GitHub Release assets. release.yml runs a dotnet build/test gate before publishing (build.yml only runs on PRs, which a direct tag push bypasses), verifies the pushed tag matches WitcherScriptMerger's hand-maintained AssemblyInfo.cs version (and that WitcherScriptMerger.Headless.csproj's <Version> matches it too) before any publish work starts, and packages the linux-x64 asset on ubuntu-latest specifically so tar can set a real Unix executable bit - building that archive on Windows/NTFS was confirmed to silently produce a non-executable binary. This was developed with AI assistance (Claude Code). The actual tag-triggered GitHub Actions run is unverified - it cannot be exercised without pushing a real tag, which this change does not do. Everything else (build, test, format, all three publishes, and merge/mcp/--version smoke tests against real Windows and real Linux via WSL) was verified directly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GXAuGMLB44T5Zv5o5ZzKah
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Unit B of the Vortex-extension groundwork: gives the parallel tool-acquisition
unit something real, versioned, and downloadable to fetch.
Properties/PublishProfiles/*.pubxml)for all three host/RID combinations:
WitcherScriptMerger(WinForms host,win-x64only),WitcherScriptMerger.Headless(win-x64andlinux-x64).Chosen over inline
.csproj<PropertyGroup>conditions specificallybecause a
.pubxmlonly activates on an explicitdotnet publish -p:PublishProfile=<name>— a plaindotnet build/dotnet publishis unaffected (verified: nowin-x64/linux-x64subfolderor self-contained runtime spew appears in a normal build).
--versionCLI flag on both hosts, checked as the very first thing(ahead of
AppSettingsconstruction, which canEnvironment.Exit(1)on amissing config file —
--versionmust still work against afreshly-extracted publish dir with no
.dll.configcopied in yet).ServerInfo.Versionon both hosts'mcpverb, via the SDK's ownModelContextProtocol.Protocol.Implementationtype (not a customside-channel) — each host also gets a distinct
ServerInfo.Nameso aclient can tell them apart.
WitcherScriptMerger.Core.VersionInfo.GetVersion(Assembly)helper (not duplicated per host, despite the two hosts' different
assembly-versioning setups —
WitcherScriptMerger.csprojhasGenerateAssemblyInfo=falseand hand-maintains its version inProperties/AssemblyInfo.cs;WitcherScriptMerger.Headless.csprojdrivesit from its own
<Version>property, kept in sync by convention)..github/workflows/release.yml: triggered onv*tag push. Four jobs:verify-version(fails fast if the tag,AssemblyInfo.cs'sAssemblyVersion, andWitcherScriptMerger.Headless.csproj's<Version>don't all three agree),
test(dotnet build+dotnet test—build.ymlonly runs on PRs, which a direct tag push bypasses),
build(a 3-entrymatrix publishing each host/RID combination, gated on
testpassing), andpackage-release(runs onubuntu-latestspecifically sotarcan set areal Unix executable bit on the
linux-x64asset — packaging that archiveon Windows/NTFS was tried and confirmed to silently produce a
non-executable binary — then creates the GitHub Release via
gh release createusing the built-inGITHUB_TOKEN).Why the version-parity design is what it is
WitcherScriptMerger'sGenerateAssemblyInfo=false(kept for a hand-writtenSupportedOSPlatform("windows")attribute alongside thenet10.0-windows7.0TFM) structurally blocks
-p:Version=from ever reaching its embeddedassembly attributes. Rather than fight that, this PR keeps the WinForms
host's version hand-maintained in
Properties/AssemblyInfo.cs(currently0.6.2), mirrors that value inWitcherScriptMerger.Headless.csproj's<Version>, and hasrelease.yml'sverify-versionjob fail loudly if apushed tag and either of those ever drift apart — turning silent version
drift into a loud CI failure instead of a footgun.
IncludeSourceRevisionInInformationalVersionis disabled on the Headless csproj so its
--version/ServerInfo.Versionprint the plain version string (
"0.6.2"), not"0.6.2+<git sha>"— matchingthe WinForms host's own (also-plain) output shape and a release tag's text
once
vis stripped.Verification
dotnet build WitcherScriptMerger.sln— succeeds; confirmed no RIDsubfolder/self-contained spew leaks into the plain build output for either
host.
dotnet test WitcherScriptMerger.sln— all 63 tests pass.dotnet format whitespace WitcherScriptMerger.sln --verify-no-changes—clean.
dotnet publish -p:PublishProfile=<name>combinations runlocally, each smoke-tested for real:
--versionprints a plain version string on all three (0.6.2forWinForms
win-x64and Headlesswin-x64/linux-x64, confirmed via areal
-p:Version=override too).mergeagainst a scratch game/mods tree (two mods editing the same.wsfile, non-overlapping changes) produces the correct auto-mergedoutput on all three, including a genuine run on real Linux via WSL2
Ubuntu-20.04 (not just a cross-compile check) for the
linux-x64build.mcpstarts cleanly on all three, logs confirm the distinctServerInfo.Nameper host, and each exits 0 on stdin close.merge/mcpverbs additionally required dummyplaceholder files at the configured
QuickBmsPath/QuickBmsPluginPath/WccLitePathto pass its combined dependency gate (never actuallyinvoked for a flat-file-only conflict).
release.ymlreviewed manually line-by-line (noactionlintavailable inthis environment); the version-guard regex and the tar-exec-bit fix were
both verified directly — the regex against the real
AssemblyInfo.csvia areal PowerShell run, and
chmod +x+tar -czfround-tripping theexecutable bit correctly on a real Linux filesystem (WSL2).
that requires pushing a real tag, which this PR does not do — the workflow
logic itself is manually reviewed and its individual pieces are verified as
above, but the full end-to-end trigger is unconfirmed until a real release
tag is pushed.
AI assistance disclosure
This PR was substantially developed with Claude Code, per this repo's
CONTRIBUTING.md. Two rounds of automated code review (plus targeted manualfixes) were applied before opening this PR — real bugs found and fixed along
the way included a version-guard regex that matched a commented-out SDK
boilerplate example instead of the real
AssemblyVersionattribute (wouldhave failed every release unconditionally),
${{ github.ref_name }}spliceddirectly into
run:script bodies instead of routed throughenv:(ascript-injection-shaped anti-pattern), the Windows/NTFS tar exec-bit issue
described above, and inconsistent version-string formatting across the two
hosts.
🤖 Generated with Claude Code
https://claude.ai/code/session_01GXAuGMLB44T5Zv5o5ZzKah