Repository navigation
Conversation
shannah
left a comment
There was a problem hiding this comment.
Thanks for tracking this down. The plumbing is clean (keeping version separate from npmVersion is the right call), but there's a design problem with what ends up in the plist.
The problem: installed bundles go stale
The .app is built once, at install time or DMG build time. After that, the launcher auto-updates the app without rebuilding the bundle. Info.plist is covered by the code signature, so it can't be patched in place on update either. So with this PR, an app installed at 1.0.138 that auto-updates to 1.0.150 keeps saying 1.0.138 in Finder → Get Info and in replace dialogs. A specific but wrong version is arguably worse than an obvious placeholder, which is why 1.0.0 has stayed hard-coded so far.
Suggested approach: only write a version that stays true for the bundle's lifetime
At install time we already know how far the app can drift: Main.java:1911 turns the user's auto-update setting into the npm range the launcher follows (createSemVerForVersion). The plist version could be derived the same way:
| Auto-update setting | npm range in app.xml |
Plist version |
|---|---|---|
Off |
exact version | real version, e.g. 1.0.138 |
PatchesOnly |
~1.0 |
1.0 |
MinorOnly |
^1 |
1 |
Stable |
latest |
placeholder (keep 1.0.0) |
branch builds (0.0.0-<branch>) |
the branch | placeholder |
Concretely:
- Add a small helper next to
createSemVerForVersionin the installer that computes this value. Set it onAppInfoas the version, instead of falling back toinitialAppVersioninBundler.createAppDescription. - On the CLI/DMG path (
PackageService.loadAppInfo), the user's update policy isn't known yet, and the app copied out of the DMG auto-updates too. So the placeholder is the honest default there, unless we can tell updates are disabled. - Strip any pre-release or build suffix before writing (
1.2.3-beta.1→1.2.3).VersionCleanerkeeps the-…suffix, andCFBundleVersion/CFBundleShortVersionStringshould be numeric (one to three dot-separated integers). Please add a test case for this.
The rest of the PR (the new AppDescription.version field, the 1.0.0 fallback in MacBundler, the test class) can stay as is. Only the value fed into it changes.
A case where the exact version can be meaningful
There's one more case where writing the exact version is fine. Publishers can set package.json properties (e.g. minInitialAppVersion, and similar launcher-version minimums) that force the full installer to run on update, and that rebuilds the bundle and its plist. If every release that matters sets these, the bundle never falls far behind. The catch is that this depends on how future releases are configured, not on anything we know at bundle time. So it can't be the default. It could be an opt-in for publishers who commit to that workflow, and it's worth noting in the docs if that route is taken.
FYI, the build-mac-x64 (17) failure is unrelated: JDeployIntegrationTest.testJDeployScript hit a DNS error (ENOTFOUND api.azul.com) downloading a JRE.
Generated by Claude Code
I updated the PR with suggested fixes I believe. I'll paste what they were below. Happy to do further testing as well. On a side-note.. I'm seeing a near future where Claude just creates issues on it's own.. then disagrees with itself and finally comes up with something that makes all of it's instances happy. And then someone will get a huge bill ;) Simplest fix The installer already computes the npm range from the auto-update setting (Main.java:1911), so the plist version can be derived from that string rather than from a second switch statement. It comes to three small edits plus a test.
The Mac guard in step 1 matters: AppInfo.version also feeds the Windows uninstall DisplayVersion (InstallWindowsRegistry.java:1040). Without the guard this PR would change Windows behaviour too. I'd skip the opt-in exact-version idea (minInitialAppVersion); shannah framed it as optional. The catch The default auto-update setting is Stable (InstallationSettings.java:44), and DMG builds get the placeholder too. So after this fix, most installs will still show 1.0.0, and only users who pick Off, Patches or Minor see a real version. If your screenshot came from a default install, this version of the PR won't change what you saw (I couldn't view the image). |
Hi @shannah - I noticed when installing an app it shows version "1.0.0" in the installer screen:

Claude tracked it down to the follow code which hardcoded "1.0.0". I asked it to fix this with the smallest changes possible -- I'm sure you've also seen AI generated code with 2 changes and 10 comments ;)
This is what it came up with and verified. I've reviewed it and it looks clean. Here's Claude's concise description of the issue (not as concise as I was hoping for but...)
macOS app bundles built by jDeploy always report version
1.0.0:MacBundler.processInfoPlistwrites that literal into bothCFBundleVersionandCFBundleShortVersionString.Updated per review: the bundle is built once and then auto-updates without being rebuilt, so an exact version would go stale. The plist now only gets the part of the version that stays true for the bundle's lifetime, derived from the npm range the installer already computes from the user's auto-update setting:
Off1.0.1381.0.138Off, pre-release1.2.3-beta.11.2.3PatchesOnly~1.01.0MinorOnly^11Stablelatest1.0.0)0.0.0-<branch>1.0.0)Changes:
Main.createBundleVersion(npmRange)(installer, next tocreateSemVerForVersion) computes the value above, stripping any pre-release or build suffix.install()sets it onAppInfo.version, on Mac only —AppInfo.versionalso feeds the Windows uninstallDisplayVersion, which this PR leaves alone.AppDescriptiongains aversionfield (kept distinct fromnpmVersion);Bundler.createAppDescriptioncopies it fromAppInfowith noinitialAppVersionfallback.MacBundlerwrites it to both plist keys, falling back to1.0.0when it is unset.PackageService.loadAppInfo) does not set a version, so DMG-built bundles keep the placeholder.MainBundleVersionTestcovers each row of the table, including suffix stripping;MacBundlerInfoPlistVersionTestcovers the versioned and fallback plist output.Not included: the opt-in exact version for publishers who force a full reinstall via
minInitialAppVersion. Note that withStableas the default setting, default installs still show1.0.0.🤖 Generated with Claude Code