fix(bump_project_version): cross-check pi.version against GVL, take the max
Recurring "GVL misread" bug in release_project_version: when
Project Information.Version drifts BEHIND the runtime-anchor GVL
(_MCP_PROJECT_VERSION.sVersion), the bump used the stale pi.version
as the resume point and silently regressed the version, often
colliding with an existing v* tag.
Observed twice on the MCPTest2 sandbox:
1. v1.0.4.0 (2026-04-26): bump from on-disk 1.2.0.0 read pi.version
as 1.0.3.0 -> revision -> 1.0.4.0. Tag deleted; recovered as
v1.2.1.0 via manual finish script.
2. v1.1.0.0 collision (2026-04-26): bump from on-disk 1.2.1.0 read
pi.version as 1.0.0.0 -> minor -> 1.1.0.0. Tag already existed,
git tag step failed, release pipeline aborted. Recovered by
two manual minor bumps (1.1.0.0 -> 1.2.0.0 -> 1.3.0.0) and an
amended commit, released as v1.3.0.0.
Root cause: the MCPTest2 v1.2.0.0 and v1.2.1.0 releases were
finished by external (non-MCP) Node scripts that updated the GVL
via inject-once but never wrote pi.version back through the
bump_project_version pathway. So pi.version stayed pinned at
whatever value the LAST true bump_project_version run left it at
(in MCPTest2's case, ~1.0.0.0), while the GVL kept moving forward.
Fix: in the pi-present branch, read both pi.version and the GVL
sVersion, parse both as 4-tuples, and take the max as the resume
point. The max is always safe: both sides only ever move forward
in the normal case, so the higher of the two is by construction
the true latest version. When a drift is detected (pi behind GVL),
emit a WARNING and self-heal pi.version forward to the GVL value
before the bump so the warning doesn't recur on the next call.
The pi-missing branch is unchanged (still falls back to GVL).
Documented inline in the function with the regression scenario for
future maintainers. No new test (the affected logic runs inside
CODESYS's IronPython and doesn't have a unit-test scaffold here);
the inline comment + this commit message are the regression record.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c50970b15e
commit
b42e10411f
1 changed files with 34 additions and 1 deletions
|
|
@ -221,7 +221,40 @@ try:
|
|||
if before_raw:
|
||||
print("DEBUG: Project Information missing, resuming from GVL: %s" % before_raw)
|
||||
else:
|
||||
before_raw = pi.version
|
||||
# Cross-check pi.version against the runtime-anchor GVL and take the
|
||||
# max of the two as the resume point. Drift between the two sides
|
||||
# happens when a release is finished by an external (non-MCP) script
|
||||
# that updates one but not the other -- the GVL gets refreshed via
|
||||
# inject-once writes, and pi.version gets refreshed via direct .project
|
||||
# binary edits, but a script that touches only one leaves the other
|
||||
# stale. Without the cross-check, the next bump would silently regress
|
||||
# the version (observed: pi.version stuck at 1.0.0.0 while GVL was at
|
||||
# 1.2.1.0 -> minor bump gave 1.1.0.0, colliding with an existing tag).
|
||||
# Taking the max is always safe: the GVL only ever moves forward (set
|
||||
# by maintain_version_gvl on every bump), and pi.version only ever
|
||||
# moves forward (set by pi.version assignment). The higher of the
|
||||
# two is the true latest version regardless of which side drifted.
|
||||
pi_raw = pi.version
|
||||
gvl_raw = read_version_from_gvl(primary_project)
|
||||
pi_parts = parse_version(pi_raw) if pi_raw is not None else (0, 0, 0, 0)
|
||||
gvl_parts = parse_version(gvl_raw) if gvl_raw else (0, 0, 0, 0)
|
||||
if pi_parts >= gvl_parts:
|
||||
before_raw = pi_raw
|
||||
else:
|
||||
print("WARNING: Project Information.Version (%s) is BEHIND the runtime anchor "
|
||||
"GVL (%s) -- this happens when a previous release was finished by an "
|
||||
"external script that updated the GVL but not the .project metadata. "
|
||||
"Using the GVL value as the resume point so the bump doesn't regress." % (
|
||||
pi_raw, gvl_raw))
|
||||
before_raw = gvl_raw
|
||||
# Heal pi.version forward to the GVL value before the bump so this
|
||||
# warning doesn't recur on the next call.
|
||||
try:
|
||||
pi.version = gvl_raw
|
||||
print("DEBUG: healed Project Information.Version: %s -> %s (matching GVL)" % (
|
||||
pi_raw, gvl_raw))
|
||||
except Exception as heal_e:
|
||||
print("WARNING: could not heal Project Information.Version: %s" % heal_e)
|
||||
before_str = str(before_raw) if before_raw is not None else None
|
||||
|
||||
# First-run convention: if no version is set yet, seed at 1.0.0.0 instead
|
||||
|
|
|
|||
Loading…
Reference in a new issue