diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f8af99e08..bb69254bcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,48 @@ followed by flat Keep-a-Changelog categories. API consumers should scan _(Release-manager scratchpad. Populated at release-cut time.)_ +## [8.1.0.01] - 2026-08-21 + +**Highlights.** The build number now matches the release name. This work has +been called VCell 8.1 in the release notes since it began, while its builds +continued the `8.0.x` sequence; from this build the two agree. This is the +first release candidate for VCell 8.1. VCell also checks properly now whether +your client matches the server it is talking to, including when the server is +updated part-way through a session — a mismatch it has been unable to detect +since 2022. + +### Added +- When the server is updated during a session, VCell now says so. The client + reconnects on its own after a deployment, and that reconnect now reports the + change and asks the user to save, exit and restart to install the matching + version. Previously the automatic reconnect was silent, so a user could work + on against a server their client no longer matched. (#2012) + +### Fixed +- VCell could not detect a client/server version mismatch that lay in the + PATCH number. `VCellSoftwareVersion` read PATCH from the MINOR position, so + the check that compares client against server was comparing MINOR twice and + ignoring PATCH entirely. Since a VCell patch release can carry a breaking + API change, this is the case that most needed catching. The line dates from + 2018 but was dead until a 2022 fix to the same statement made it live. + (#2011) + +### Changed +- Version numbering realigned with the release narrative: builds are numbered + `8.1.x` from here, rather than continuing `8.0.x`. Published numbers up to + 8.0.28.01 are unchanged. (#2010) +- The client/server version warning now appears whenever MAJOR, MINOR **or** + PATCH differ — which is what it was always meant to do, and could not. + Expect to see it more often than before: it was effectively dormant across + the whole 8.0 line, and a patch difference alone is now enough to raise it. + The warning is advisory and does not prevent use. Only the BUILD number is + ignored. (#2012) + +### Notes for API consumers +No API changes. Clients that parse the version string should note that +`MAJOR.MINOR` moves from `8.0` to `8.1`; the four-part +`MAJOR.MINOR.PATCH.BUILD` shape is unchanged. + ## [8.0.28.01] - 2026-08-20 **Highlights.** Dragging in the SpringSaLaD trajectory viewer turned the scene the wrong way — diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 1f8196b04b..9929b22769 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -122,12 +122,14 @@ This has already happened once. **VCell 8.0** is the 8.0.0 builds only — 8.0.0.01 through 8.0.0.03, the SpringSaLaD GA that ran in production from 2026-05-21 to 2026-08-14. Everything released after 8.0.0.03 is **VCell 8.1**, even though those builds are numbered 8.0.2.01 through -8.0.28.01 and beyond. +8.0.28.01. The numbering was realigned at **8.1.0.01**, so the two agree +again from that build onward; the divergence covers 8.0.2.01–8.0.28.01 +only. So, at release-cut time, add the user-facing items to the narrative file for the release **currently being accumulated** — today -`8.1.md` — not to the file whose name matches the build's -`MAJOR.MINOR`. `CHANGELOG.md` is unaffected: it is organised strictly +`8.1.md` — which is not necessarily the file whose name matches the +build's `MAJOR.MINOR`. `CHANGELOG.md` is unaffected: it is organised strictly by build version and always records the real number. When a long-running `MAJOR.MINOR` line accumulates multiple distinct diff --git a/release-notes/major/8.1.md b/release-notes/major/8.1.md index aec6adeb8b..68203c3ac0 100644 --- a/release-notes/major/8.1.md +++ b/release-notes/major/8.1.md @@ -1,15 +1,19 @@ # VCell 8.1 -**Status:** pre-release. Rolled to the production site on 2026-08-14 -and updated since; not yet announced as a public release event. - -> **A note on version numbers.** VCell 8.1 is delivered by builds whose -> version numbers continue the `8.0.x` sequence — 8.0.2.01 through -> 8.0.28.01 so far. Those numbers are what they are and are not being -> restated. For release notes and user-facing announcements, **VCell 8.0 -> means the 8.0.0 builds** (8.0.0.01–8.0.0.03, the SpringSaLaD GA that -> ran in production from 2026-05-21 to 2026-08-14), and **everything -> released after 8.0.0.03 is VCell 8.1**. +**Status:** release candidate — **8.1.0.01**. An earlier form of this +release has been on the production site since 2026-08-14 under `8.0.x` +build numbers; from 8.1.0.01 the build number carries the release name. + +> **A note on version numbers.** VCell 8.1 was developed under build +> numbers that continued the `8.0.x` sequence — 8.0.2.01 through +> 8.0.28.01. Those numbers are published and are not being restated. +> **From 8.1.0.01 the two agree**, and the explanation below applies +> only to the builds before it. +> +> For release notes and user-facing announcements, **VCell 8.0 means +> the 8.0.0 builds** (8.0.0.01–8.0.0.03, the SpringSaLaD GA that ran in +> production from 2026-05-21 to 2026-08-14), and **everything released +> after 8.0.0.03 is VCell 8.1**. ## Headline @@ -72,6 +76,12 @@ where spatial visualization is heading, not because it is ready. ## Improvements +- VCell now checks properly whether your client matches the server, and says + so when it does not — including when the server is updated part-way through + a session, which it previously reconnected to in silence. A VCell patch + release can change the API, so a client and server differing in any part of + the version may not work together; the check had been unable to see a + difference in the patch number since 2022 (8.1.0.01). - Desktop window behaviour: child windows and dialogs now stay in front of the window that opened them — and minimize and travel with it — on modern macOS and Windows, where they could previously slip behind the diff --git a/vcell-apiclient/src/main/java/org/vcell/api/server/ClientServerManager.java b/vcell-apiclient/src/main/java/org/vcell/api/server/ClientServerManager.java index 67eb1ff9fd..9ff0395c54 100644 --- a/vcell-apiclient/src/main/java/org/vcell/api/server/ClientServerManager.java +++ b/vcell-apiclient/src/main/java/org/vcell/api/server/ClientServerManager.java @@ -275,7 +275,18 @@ public MessageEvent[] getMessageEvents() throws RemoteProxyException, IOExceptio } } -private void checkClientServerSoftwareVersion(InteractiveClientServerContext requester, ClientServerInfo clientServerInfo) { +/** + * Why the version check is running. The comparison is the same either way -- any + * difference in MAJOR, MINOR or PATCH -- but the two situations need different advice. + */ +private enum VersionCheck { + /** At login, or a reconnect the user asked for: the client was already out of date. */ + ON_CONNECT, + /** After an automatic reconnect: the server was redeployed underneath a running client. */ + AFTER_SERVER_CHANGED +} + +private void checkClientServerSoftwareVersion(InteractiveClientServerContext requester, ClientServerInfo clientServerInfo, VersionCheck when) { String clientSoftwareVersion = System.getProperty(PropertyLoader.vcellSoftwareVersion); if (clientSoftwareVersion != null && clientSoftwareVersion.toLowerCase().contains("devel") ) { return; @@ -293,13 +304,27 @@ private void checkClientServerSoftwareVersion(InteractiveClientServerContext req + "We have adopted a Release Early, Release Often software development approach\n\n" + "\nPlease exit VCell and download the latest client from VCell Software page (http://vcell.org)."); } + // + // MAJOR, MINOR and PATCH all matter. A VCell PATCH release can carry a breaking + // API change, so a client and server differing in any of the three are not known + // to be compatible. Only BUILD is ignored. This is what the check has always + // meant to do; it could not, because getPatchVersion() returned MINOR (#2011). + // if (clientVersion.getMajorVersion()!=serverVersion.getMajorVersion() || clientVersion.getMinorVersion()!=serverVersion.getMinorVersion() || clientVersion.getPatchVersion()!=serverVersion.getPatchVersion()) { - requester.showWarningDialog("software version mismatch between client and server:\n" - + "client VCell version : " + clientSoftwareVersion + "\n" - + "server VCell version : " + serverSoftwareVersion + "\n" - + "\nPlease exit VCell and download the latest client from VCell Software page (http://vcell.org)."); + if (when == VersionCheck.AFTER_SERVER_CHANGED) { + requester.showWarningDialog("The VCell server was updated while you were working:\n" + + "client VCell version : " + clientSoftwareVersion + "\n" + + "server VCell version : " + serverSoftwareVersion + "\n" + + "\nThis client no longer matches the server." + + "\nPlease save your work, then exit and restart VCell to install the matching version."); + } else { + requester.showWarningDialog("software version mismatch between client and server:\n" + + "client VCell version : " + clientSoftwareVersion + "\n" + + "server VCell version : " + serverSoftwareVersion + "\n" + + "\nPlease exit VCell and download the latest client from VCell Software page (http://vcell.org)."); + } } } } @@ -318,7 +343,7 @@ public void connectNewServer(InteractiveClientServerContext requester, ClientSer public void connect(InteractiveClientServerContext requester) { asynchMessageManager.stopPolling(); reconnectStat = ReconnectStatus.NOT; - checkClientServerSoftwareVersion(requester,clientServerInfo); + checkClientServerSoftwareVersion(requester,clientServerInfo,VersionCheck.ON_CONNECT); // get new server connection VCellConnection newVCellConnection = connectToServer(requester,true); @@ -727,6 +752,12 @@ void reconnect() { changeConnection(requester,connection); rc.stop(); asynchMessageManager.startPolling(); + // + // Only once the reconnect has succeeded -- a failed attempt says nothing about + // the server's version, and this check makes an HTTP call of its own that the + // retry loop should not repeat. + // + checkClientServerSoftwareVersion(requester,clientServerInfo,VersionCheck.AFTER_SERVER_CHANGED); return; } setConnectionStatus(new ClientConnectionStatus(getClientServerInfo().getUsername(), getClientServerInfo().getApihost(), getClientServerInfo().getApiport(), ConnectionStatus.DISCONNECTED)); diff --git a/vcell-core/src/main/java/org/vcell/util/document/VCellSoftwareVersion.java b/vcell-core/src/main/java/org/vcell/util/document/VCellSoftwareVersion.java index 0b47cac377..060db74a37 100644 --- a/vcell-core/src/main/java/org/vcell/util/document/VCellSoftwareVersion.java +++ b/vcell-core/src/main/java/org/vcell/util/document/VCellSoftwareVersion.java @@ -77,7 +77,7 @@ private VCellSoftwareVersion(String softwareVersionString){ minorVersion = safeParse(minor); } if (parts.length > 2) { - String patch = parts[1]; + String patch = parts[2]; patchVersion = safeParse(patch); } } catch (Exception exc) { diff --git a/vcell-core/src/test/java/org/vcell/util/document/VCellSoftwareVersionTest.java b/vcell-core/src/test/java/org/vcell/util/document/VCellSoftwareVersionTest.java new file mode 100644 index 0000000000..e3306ebcbe --- /dev/null +++ b/vcell-core/src/test/java/org/vcell/util/document/VCellSoftwareVersionTest.java @@ -0,0 +1,44 @@ +package org.vcell.util.document; + +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * The four-part version is carried as "_Version__build_". + * PATCH was read from the MINOR position for four years (issue #2011) without anything + * noticing, because its only consumer compared it against a value that was equally wrong. + */ +@Tag("Fast") +public class VCellSoftwareVersionTest { + + @Test + public void parsesAllFourParts() { + VCellSoftwareVersion v = VCellSoftwareVersion.fromString("Alpha_Version_8.0.28_build_01"); + assertEquals(VCellSoftwareVersion.VCellSite.alpha, v.getSite()); + assertEquals(8, v.getMajorVersion()); + assertEquals(0, v.getMinorVersion()); + assertEquals(28, v.getPatchVersion()); + assertEquals("01", v.getBuildNumber()); + } + + @Test + public void patchIsNotAliasedToMinor() { + // 8.1.0 and 8.0.1 differ only by which of the two middle numbers is set, so a + // parser that reads PATCH from the MINOR position cannot tell them apart. + VCellSoftwareVersion a = VCellSoftwareVersion.fromString("Rel_Version_8.1.0_build_01"); + assertEquals(1, a.getMinorVersion()); + assertEquals(0, a.getPatchVersion()); + + VCellSoftwareVersion b = VCellSoftwareVersion.fromString("Rel_Version_8.0.1_build_01"); + assertEquals(0, b.getMinorVersion()); + assertEquals(1, b.getPatchVersion()); + } + + @Test + public void unparseableVersionIsUnknownRatherThanThrowing() { + VCellSoftwareVersion v = VCellSoftwareVersion.fromString("nonsense"); + assertEquals(VCellSoftwareVersion.VCellSite.unknown, v.getSite()); + } +}