Skip to content

Add release workflow, self-contained publish profiles, and version exposure - #16

Merged
TheValiantOne merged 1 commit into
mainfrom
feature/release-workflow-and-version-clean
Aug 9, 2026
Merged

Add release workflow, self-contained publish profiles, and version exposure#16
TheValiantOne merged 1 commit into
mainfrom
feature/release-workflow-and-version-clean

Conversation

@TheValiantOne

Copy link
Copy Markdown
Owner

Summary

Unit B of the Vortex-extension groundwork: gives the parallel tool-acquisition
unit something real, versioned, and downloadable to fetch.

  • Self-contained single-file publish profiles (Properties/PublishProfiles/*.pubxml)
    for all three host/RID combinations: WitcherScriptMerger (WinForms host,
    win-x64 only), WitcherScriptMerger.Headless (win-x64 and linux-x64).
    Chosen over inline .csproj <PropertyGroup> conditions specifically
    because a .pubxml only activates on an explicit
    dotnet publish -p:PublishProfile=<name> — a plain dotnet build/
    dotnet publish is unaffected (verified: no win-x64/linux-x64 subfolder
    or self-contained runtime spew appears in a normal build).
  • --version CLI flag on both hosts, checked as the very first thing
    (ahead of AppSettings construction, which can Environment.Exit(1) on a
    missing config file — --version must still work against a
    freshly-extracted publish dir with no .dll.config copied in yet).
  • MCP ServerInfo.Version on both hosts' mcp verb, via the SDK's own
    ModelContextProtocol.Protocol.Implementation type (not a custom
    side-channel) — each host also gets a distinct ServerInfo.Name so a
    client can tell them apart.
  • Both of the above are backed by a new shared WitcherScriptMerger.Core.VersionInfo.GetVersion(Assembly)
    helper (not duplicated per host, despite the two hosts' different
    assembly-versioning setups — WitcherScriptMerger.csproj has
    GenerateAssemblyInfo=false and hand-maintains its version in
    Properties/AssemblyInfo.cs; WitcherScriptMerger.Headless.csproj drives
    it from its own <Version> property, kept in sync by convention).
  • .github/workflows/release.yml: triggered on v* tag push. Four jobs:
    verify-version (fails fast if the tag, AssemblyInfo.cs's
    AssemblyVersion, and WitcherScriptMerger.Headless.csproj's <Version>
    don't all three agree), test (dotnet build + dotnet testbuild.yml
    only runs on PRs, which a direct tag push bypasses), build (a 3-entry
    matrix publishing each host/RID combination, gated on test passing), and
    package-release (runs on ubuntu-latest specifically so tar can set a
    real Unix executable bit on the linux-x64 asset — packaging that archive
    on Windows/NTFS was tried and confirmed to silently produce a
    non-executable binary — then creates the GitHub Release via gh release create using the built-in GITHUB_TOKEN).

Why the version-parity design is what it is

WitcherScriptMerger's GenerateAssemblyInfo=false (kept for a hand-written
SupportedOSPlatform("windows") attribute alongside the net10.0-windows7.0
TFM) structurally blocks -p:Version= from ever reaching its embedded
assembly attributes. Rather than fight that, this PR keeps the WinForms
host's version hand-maintained in Properties/AssemblyInfo.cs (currently
0.6.2), mirrors that value in WitcherScriptMerger.Headless.csproj's
<Version>, and has release.yml's verify-version job fail loudly if a
pushed tag and either of those ever drift apart — turning silent version
drift into a loud CI failure instead of a footgun. IncludeSourceRevisionInInformationalVersion
is disabled on the Headless csproj so its --version/ServerInfo.Version
print the plain version string ("0.6.2"), not "0.6.2+<git sha>" — matching
the WinForms host's own (also-plain) output shape and a release tag's text
once v is stripped.

Verification

  • dotnet build WitcherScriptMerger.sln — succeeds; confirmed no RID
    subfolder/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.
  • All three dotnet publish -p:PublishProfile=<name> combinations run
    locally, each smoke-tested for real:
    • --version prints a plain version string on all three (0.6.2 for
      WinForms win-x64 and Headless win-x64/linux-x64, confirmed via a
      real -p:Version= override too).
    • merge against a scratch game/mods tree (two mods editing the same
      .ws file, non-overlapping changes) produces the correct auto-merged
      output on all three, including a genuine run on real Linux via WSL2
      Ubuntu-20.04 (not just a cross-compile check) for the linux-x64 build.
    • mcp starts cleanly on all three, logs confirm the distinct
      ServerInfo.Name per host, and each exits 0 on stdin close.
    • The WinForms host's merge/mcp verbs additionally required dummy
      placeholder files at the configured QuickBmsPath/QuickBmsPluginPath/
      WccLitePath to pass its combined dependency gate (never actually
      invoked for a flat-file-only conflict).
  • release.yml reviewed manually line-by-line (no actionlint available in
    this environment); the version-guard regex and the tar-exec-bit fix were
    both verified directly — the regex against the real AssemblyInfo.cs via a
    real PowerShell run, and chmod +x + tar -czf round-tripping the
    executable bit correctly on a real Linux filesystem (WSL2).
  • Not verified: the actual tag-triggered GitHub Actions run. Exercising
    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 manual
fixes) 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 AssemblyVersion attribute (would
have failed every release unconditionally), ${{ github.ref_name }} spliced
directly into run: script bodies instead of routed through env: (a
script-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

…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
@TheValiantOne
TheValiantOne merged commit 526331c into main Aug 9, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant