Skip to content

Strip the pre-release suffix from the MSI version - #536

Open
leynos wants to merge 1 commit into
mainfrom
fix-msi-prerelease-version
Open

Strip the pre-release suffix from the MSI version#536
leynos wants to merge 1 commit into
mainfrom
fix-msi-prerelease-version

Conversation

@leynos

@leynos leynos commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Every pull request's release / build-windows check currently fails with Invalid MSI version '0.1.0-beta1': MSI ProductVersion accepts only numeric major.minor.build, and the pinned windows-package shared action validates that before building. Main never runs the release dry-run on push, so only PRs surface it.

The Windows packaging step now derives a numeric version by stripping the pre-release identifier (0.1.0-beta10.1.0) for the installer alone; Linux, macOS, and archive artefacts keep the full crate version. Workflow contract tests (workflow_build_and_package, polonius_toolchain_contract, workflow_release) pass unchanged.

An estate-level alternative — teaching the shared action a pre-release mapping — can supersede this, but the local derivation unblocks all open PRs now.

🤖 Generated with Claude Code

Summary by Sourcery

Build:

  • Adjust the build-and-package GitHub Actions workflow to compute a numeric MSI version from the crate version by dropping any pre-release suffix and pass that to the windows-package action for Windows installers.

MSI ProductVersion accepts only numeric major.minor.build, so the
pinned windows-package action rejects the crate version 0.1.0-beta1
and every pull request's release dry-run fails at the installer step.
The Windows packaging step now derives its version by stripping the
pre-release identifier; every other artefact keeps the full crate
version, and the workflow contract tests still pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @leynos, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@sourcery-ai

sourcery-ai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Adjusts the Windows build-and-package workflow so that the MSI installer uses a numeric-only version (without pre-release suffix), while other artifacts keep the full semantic version, to satisfy MSI ProductVersion constraints and unblock release checks.

Sequence diagram for deriving numeric MSI version in Windows workflow

sequenceDiagram
    participant Workflow as GitHubActionsWorkflow
    participant Derive as DeriveNumericMSIVersionStep
    participant WindowsPackage as WindowsPackageAction

    Workflow->>Derive: inputs.version
    Derive-->>Workflow: msi_version.outputs.value (version%%-*)
    Workflow->>WindowsPackage: version = msi_version.outputs.value
    WindowsPackage-->>Workflow: Build MSI with numeric ProductVersion
Loading

File-Level Changes

Change Details Files
Derive and use a numeric-only MSI version for the Windows installer step in the build-and-package workflow.
  • Add a Windows-only step that strips any pre-release suffix from the input version to produce a numeric MSI-compatible version using bash parameter expansion
  • Expose the derived numeric version via the step’s GITHUB_OUTPUT for downstream steps
  • Update the Windows installer packaging step to consume the derived MSI version instead of the raw input version
.github/workflows/build-and-package.yml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Summary

  • Strip the pre-release suffix from the crate version for Windows MSI packages.
  • Preserve the full crate version for Linux, macOS, and archive artifacts.
  • Keep workflow contract tests unchanged and passing.

Walkthrough

Update the Windows packaging workflow to strip pre-release suffixes from inputs.version. Pass the resulting numeric version to the MSI installer.

Changes

Windows MSI packaging

Layer / File(s) Summary
Derive and apply the MSI version
.github/workflows/build-and-package.yml
Add a Windows-only msi_version step. Strip the pre-release suffix and expose the numeric result through GITHUB_OUTPUT. Pass this result to the Windows installer.

Possibly related issues

  • leynos/shared-actions issue 405 — Addresses the same Windows MSI pre-release version handling.

Possibly related PRs

  • leynos/shared-actions#406 — Updates Windows MSI packaging to use a numeric version after removing pre-release metadata.
  • leynos/netsuke#535 — Updates the shared action involved in this workflow’s MSI version handling.

Poem

Strip the suffix, keep numbers bright,
Pass the clean version through the build night.
Let Windows package without delay,
And send the MSI safely on its way.


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error, 5 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Testing (Overall) ❌ Error The PR adds MSI version derivation, but no test covers it; workflow tests contain no MSI/version oracle and would pass with the old direct input wiring. Add a workflow contract test that asserts the derivation step strips -beta1, passes 0.1.0 to windows-package, and preserves the full version for other packages.
Developer Documentation ⚠️ Warning The commit changes only the packaging workflow and adds an MSI version boundary, but no developer-guide, design-document, or ADR entry records the new pre-release handling rule. Add the MSI version rule to docs/developers-guide.md, link it to the Windows packaging workflow, and record the decision in the relevant design document or ADR if required.
Testing (Unit And Behavioural) ⚠️ Warning The PR changes only the workflow; no test files changed, and existing workflow tests do not reference msi_version or the suffix-stripping behaviour. Add a behavioural workflow test at the packaging boundary. Cover stable and pre-release versions, separator edge cases, invalid input handling, and preservation of full versions for non-Windows artefacts.
Testing (Property / Proof) ⚠️ Warning The change adds a version-string invariant in .github/workflows/build-and-package.yml:165-166, but the PR adds no property-based coverage or recommendation; existing workflow tests remain unchanged. Add a substantive proptest for valid Cargo version strings, covering stable and varied pre-release suffixes, and verify the derived MSI value is numeric and preserves the base version.
Testing (Compile-Time / Ui) ⚠️ Warning The PR has no Rust or TypeScript compile behaviour, but its new structured MSI-version derivation has no focused contract or snapshot test. Add a focused workflow contract test for pre-release and stable versions, and verify that Linux and macOS still use the full version.
Observability ⚠️ Warning The new MSI-version decision emits only a GITHUB_OUTPUT value; it logs neither source nor derived version and adds no validation or metric for packaging failures. Log the original and derived MSI versions at the derivation step, validate the numeric major.minor.build format, and emit a clear workflow error before windows-package runs.
User-Facing Documentation ❓ Inconclusive Investigation in progress. Inspect the user guide and release workflow to determine whether the MSI version change is user-facing behaviour requiring documentation.
✅ Passed checks (13 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: removing the pre-release suffix from the MSI version.
Description check ✅ Passed The description directly explains the MSI version issue, the workflow fix, and its effect on other artefacts.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Module-Level Documentation ✅ Passed The PR changes only a GitHub Actions YAML workflow and adds no module; the repository’s Rust modules already carry module-level documentation.
Unit Architecture ✅ Passed The Windows-only step has an explicit version input and GITHUB_OUTPUT output, and only the MSI consumes it; Linux and macOS retain inputs.version. No hidden state or unrelated responsibility was ad...
Domain Architecture ✅ Passed Keep this change: the commit modifies only the workflow adapter, translates the version for MSI, and leaves Cargo/runtime domain code and full versions unchanged.
Security And Privacy ✅ Passed Pass this check: the diff only derives an MSI version from the Cargo version and adds no secrets, credentials, permissions, sensitive data, or new external sinks.
Performance And Resource Use ✅ Passed The Windows-only step performs one bounded Bash parameter expansion and one GITHUB_OUTPUT write; it adds no loops, collections, retries, blocking work, or repeated I/O.
Concurrency And State ✅ Passed Accept the change: the step runs sequentially before windows-package, passes one GITHUB_OUTPUT value, and each matrix job has isolated state; no new shared mutable state or async lifetime exists.
Architectural Complexity And Maintainability ✅ Passed Keep this change: it adds one Windows-only Bash step, uses existing GITHUB_OUTPUT wiring, adds no dependency or abstraction, and leaves Linux/macOS versions unchanged.
Rust Compiler Lint Integrity ✅ Passed The commit changes only .github/workflows/build-and-package.yml; it has no Rust diff, lint suppressions, or clone additions to assess.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-msi-prerelease-version

Comment @coderabbitai help to get the list of available commands.

codescene-access[bot]

This comment was marked as outdated.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/build-and-package.yml:
- Line 165: Update the workflow step around the version assignment to pass the
caller-provided version through an environment variable before Bash evaluates
it, then read it from INPUT_VERSION. Validate the value against strict SemVer
and only write GITHUB_OUTPUT after validation succeeds.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2bcd5cef-bc43-405f-9e93-dce1e4263a90

📥 Commits

Reviewing files that changed from the base of the PR and between 8ac4103 and 83bcdbd.

📒 Files selected for processing (1)
  • .github/workflows/build-and-package.yml
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • leynos/rstest-bdd (auto-detected)
  • leynos/ortho-config (auto-detected)
  • leynos/whitaker (auto-detected)
  • leynos/shared-actions (auto-detected)

# 0.1.0-beta1. The suffix is stripped for the installer alone; every
# other artefact keeps the full crate version.
run: |
version="${{ inputs.version }}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' 'Reusable workflow and callers:'
rg -n -C 10 \
  'build-and-package\.yml|needs\.metadata\.outputs\.version|outputs:|version:|cargo metadata|semver|validate.*version|version.*validate' \
  .github/workflows

printf '%s\n' 'Relevant workflow sections:'
sed -n '130,190p' .github/workflows/build-and-package.yml
sed -n '1,130p' .github/workflows/release.yml

Repository: leynos/netsuke

Length of output: 31304


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' 'Cargo version action files:'
git ls-files '.github/actions/ensure-cargo-version' '.github/actions/export-cargo-metadata'
find .github/actions -maxdepth 2 -type f \( -iname '*cargo*version*' -o -iname '*cargo*metadata*' \) -print

printf '%s\n' 'Version extraction and validation references:'
rg -n -C 8 \
  'ensure-cargo-version|crate-version|export-cargo-metadata|cargo metadata|semver|version.*valid|validate.*version|check-tag' \
  .github/actions .github/workflows

Repository: leynos/shared-actions

Length of output: 50379


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' 'ensure-cargo-version action:'
cat -n .github/actions/ensure-cargo-version/action.yml
printf '%s\n' 'ensure_cargo_version.py:'
cat -n .github/actions/ensure-cargo-version/scripts/ensure_cargo_version.py
printf '%s\n' 'export-cargo-metadata action:'
cat -n .github/actions/export-cargo-metadata/action.yml
printf '%s\n' 'read_manifest.py:'
cat -n .github/actions/export-cargo-metadata/scripts/read_manifest.py

Repository: leynos/shared-actions

Length of output: 22928


🏁 Script executed on selected repositories:

#!/bin/bash
set -euo pipefail

printf '%s\n' 'Release-mode decision and callers:'
rg -n -C 12 \
  'determine-release-modes|should-publish|workflow_call|release\.yml|netsuke.*release|uses:.*release' \
  .github/actions .github/workflows README.md 2>/dev/null || true

printf '%s\n' 'Exact release-mode action:'
find .github/actions -maxdepth 3 -type f -path '*determine-release-modes*' -print

Repositories: leynos/netsuke, leynos/shared-actions

Length of output: 63879


🏁 Script executed (no clone):

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import os
import subprocess

payload = '0.1.0"; printf "INJECTED\\n"; #'
unsafe_script = f'version="{payload}"\nprintf "version=%s\\n" "$version"\n'
safe_script = 'version="$INPUT_VERSION"\nprintf "version=%s\\n" "$version"\n'

unsafe = subprocess.run(
    ["bash", "-c", unsafe_script],
    text=True,
    capture_output=True,
    check=False,
)
safe = subprocess.run(
    ["bash", "-c", safe_script],
    env={**os.environ, "INPUT_VERSION": payload},
    text=True,
    capture_output=True,
    check=False,
)

print("unsafe_returncode:", unsafe.returncode)
print("unsafe_stdout:", repr(unsafe.stdout))
print("safe_returncode:", safe.returncode)
print("safe_stdout:", repr(safe.stdout))

assert "INJECTED" in unsafe.stdout
assert "INJECTED" not in safe.stdout
assert payload in safe.stdout
PY

Length of output: 354


Injection (CWE-78): Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')

Reachability: External · Exploitability: Trivial

Pass inputs.version through an environment variable before Bash parses the script.

The reusable workflow accepts version from callers. Shell syntax in this value can execute commands on the Windows runner. Read version="$INPUT_VERSION" and validate strict SemVer before writing GITHUB_OUTPUT.

🧰 Tools
🪛 zizmor (1.29.0)

[error] 165-165: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/build-and-package.yml at line 165, Update the workflow
step around the version assignment to pass the caller-provided version through
an environment variable before Bash evaluates it, then read it from
INPUT_VERSION. Validate the value against strict SemVer and only write
GITHUB_OUTPUT after validation succeeds.

Source: Linters/SAST tools

@codescene-access codescene-access Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No quality gates enabled for this code.

@leynos

leynos commented Aug 6, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai is this now resolved in main by the shared-actions bump?

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- PR metadata ---'
gh pr view 536 --repo leynos/netsuke \
  --json number,state,mergedAt,baseRefName,headRefName,url \
  --jq '{number, state, mergedAt, baseRefName, headRefName, url}'

echo '--- main workflow shared-action references ---'
git show origin/main:.github/workflows/build-and-package.yml |
  rg -n -C3 'shared-actions|windows-package|version'

echo '--- PR workflow diff ---'
gh pr diff 536 --repo leynos/netsuke -- .github/workflows/build-and-package.yml

echo '--- shared-actions remote refs and windows-package history ---'
git ls-remote https://github.com/leynos/shared-actions.git 'refs/heads/main'

Length of output: 5777


🏁 Script executed:

#!/bin/bash
set -euo pipefail

PIN='8add2d99854a5b77548eae98cca59202e68fefc8'
ACTION_PATH='.github/actions/windows-package/action.yml'

echo '--- pinned windows-package action ---'
gh api "repos/leynos/shared-actions/contents/${ACTION_PATH}?ref=${PIN}" \
  --jq '.content' | base64 --decode |
  rg -n -C5 'ProductVersion|pre-release|prerelease|VERSION|version'

echo '--- PR `#536` changed workflow patch ---'
gh api 'repos/leynos/netsuke/pulls/536/files?per_page=100' \
  --jq '.[] | select(.filename == ".github/workflows/build-and-package.yml") | .patch'

echo '--- commit identity and ancestry ---'
gh api "repos/leynos/shared-actions/commits/${PIN}" \
  --jq '{sha: .sha, message: .commit.message, date: .commit.author.date}'

Length of output: 11850


@leynos Yes. The shared-actions bump resolves this on main.

main pins windows-package to 8add2d99854a5b77548eae98cca59202e68fefc8. That revision strips SemVer pre-release and build metadata before it sets the MSI ProductVersion. For example, it converts 0.1.0-beta1 to 0.1.0.

Keep passing the full crate version from the workflow. The action normalizes only the MSI internal version. Other artifact versions remain unchanged.

Close PR #536 as redundant. Its workflow-level workaround duplicates the shared action behaviour.

You are interacting with an AI system.

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