diff --git a/src/components/upgrade-instructions/DockerComposeUpgradeSteps.astro b/src/components/upgrade-instructions/DockerComposeUpgradeSteps.astro index 940f536c7b..ba04bd73a1 100644 --- a/src/components/upgrade-instructions/DockerComposeUpgradeSteps.astro +++ b/src/components/upgrade-instructions/DockerComposeUpgradeSteps.astro @@ -1,6 +1,6 @@ --- import { Code } from '@astrojs/starlight/components'; -import { UPGRADE_VERSIONS, getFamilySlug } from '~/models/upgrade-instructions'; +import { UPGRADE_VERSIONS, LATEST_PATCH_VERSIONS, getFamilySlug } from '~/models/upgrade-instructions'; import type { Products } from '~/models/site.models'; interface Props { @@ -56,8 +56,8 @@ function repoLink(ver: string): string | null { {versions.map((v) => { const newer = isNewerVersionScheme(v.version); const needsCompat = needsCompatibilityCheck(v.version); - const heading = v.patch && v.baseVersion !== v.displayVersion - ? `Upgrading ThingsBoard${isPE ? ' PE' : ''} to latest ${v.baseVersion} (${v.displayVersion})` + const heading = v.patch && v.baseVersion !== v.displayVersion && LATEST_PATCH_VERSIONS.has(v.version) + ? `Upgrading ThingsBoard${isPE ? ' PE' : ''} to ${v.displayVersion} (latest ${v.baseVersion} patch)` : `Upgrading ThingsBoard${isPE ? ' PE' : ''} to ${v.displayVersion}`; const prevLabel = v.x ? v.upgradableFrom : v.upgradableFrom; const prevDisplayLabel = isPE ? `${prevLabel} PE` : prevLabel; @@ -117,7 +117,7 @@ function repoLink(ver: string): string | null { {prevHref ? ( {prevDisplayLabel} ) : prevDisplayLabel} - {v.patch && <>{' '}or any {v.baseVersion} patch}.{' '} + {v.patch && <>{' '}or any {v.patchableFrom ?? v.baseVersion} patch}.{' '} In order to upgrade to {isPE ? `${v.displayVersion}PE` : v.displayVersion} you need to{' '} {prevHref ? ( <>upgrade to {prevDisplayLabel} first. @@ -161,7 +161,7 @@ function repoLink(ver: string): string | null {
{v.patch ? ( -

If you are upgrading from {v.upgradableFrom}, you must run the script below. However, if you are upgrading from version {`${v.baseVersion}.x`}, DO NOT run the upgrade script; proceed directly to starting the service.

+

If you are upgrading from {v.upgradableFrom}, you must run the script below. However, if you are upgrading from version {v.patchableFrom ?? v.baseVersion + '.x'}, DO NOT run the upgrade script; proceed directly to starting the service.

) : (

If you are upgrading from version {v.upgradableFrom}, you must run the script below.

)} diff --git a/src/components/upgrade-instructions/DockerUpgradeSteps.astro b/src/components/upgrade-instructions/DockerUpgradeSteps.astro index ae3d70e57d..af58445041 100644 --- a/src/components/upgrade-instructions/DockerUpgradeSteps.astro +++ b/src/components/upgrade-instructions/DockerUpgradeSteps.astro @@ -1,6 +1,6 @@ --- import { Code } from '@astrojs/starlight/components'; -import { UPGRADE_VERSIONS, getFamilySlug } from '~/models/upgrade-instructions'; +import { UPGRADE_VERSIONS, LATEST_PATCH_VERSIONS, getFamilySlug } from '~/models/upgrade-instructions'; import type { Products } from '~/models/site.models'; interface Props { @@ -34,8 +34,8 @@ function versionTag(ver: string): string { {versions.map((v) => { const newer = isNewerVersionScheme(v.version); const needsCompat = needsCompatibilityCheck(v.version); - const heading = v.patch && v.baseVersion !== v.displayVersion - ? `Upgrading ThingsBoard${isPE ? ' PE' : ''} to latest ${v.baseVersion} (${v.displayVersion})` + const heading = v.patch && v.baseVersion !== v.displayVersion && LATEST_PATCH_VERSIONS.has(v.version) + ? `Upgrading ThingsBoard${isPE ? ' PE' : ''} to ${v.displayVersion} (latest ${v.baseVersion} patch)` : `Upgrading ThingsBoard${isPE ? ' PE' : ''} to ${v.displayVersion}`; const prevLabel = v.x ? v.upgradableFrom : v.upgradableFrom; const prevDisplayLabel = isPE ? `${prevLabel} PE` : prevLabel; @@ -94,7 +94,7 @@ function versionTag(ver: string): string { {prevHref ? ( {prevDisplayLabel} ) : prevDisplayLabel} - {v.patch && <>{' '}or any {v.baseVersion} patch}.{' '} + {v.patch && <>{' '}or any {v.patchableFrom ?? v.baseVersion} patch}.{' '} In order to upgrade to {isPE ? `${v.displayVersion}PE` : v.displayVersion} you need to{' '} {prevHref ? ( <>upgrade to {prevDisplayLabel} first. @@ -122,7 +122,7 @@ function versionTag(ver: string): string {
{v.patch ? ( -

If you are upgrading from {v.upgradableFrom}, you must run the script below. However, if you are upgrading from version {`${v.baseVersion}.x`}, DO NOT run the upgrade script; proceed directly to starting the service.

+

If you are upgrading from {v.upgradableFrom}, you must run the script below. However, if you are upgrading from version {v.patchableFrom ?? v.baseVersion + '.x'}, DO NOT run the upgrade script; proceed directly to starting the service.

) : (

If you are upgrading from version {v.upgradableFrom}, you must run the script below.

)} @@ -144,7 +144,7 @@ function versionTag(ver: string): string {
{v.patch ? ( -

If you are upgrading from {v.upgradableFrom}, you must run the script below. However, if you are upgrading from version {`${v.baseVersion}.x`}, DO NOT run the upgrade script; proceed directly to starting the service.

+

If you are upgrading from {v.upgradableFrom}, you must run the script below. However, if you are upgrading from version {v.patchableFrom ?? v.baseVersion + '.x'}, DO NOT run the upgrade script; proceed directly to starting the service.

) : (

If you are upgrading from version {v.upgradableFrom}, you must run the script below.

)} diff --git a/src/components/upgrade-instructions/LinuxUpgradeSteps.astro b/src/components/upgrade-instructions/LinuxUpgradeSteps.astro index 8361c47ef9..00e201154e 100644 --- a/src/components/upgrade-instructions/LinuxUpgradeSteps.astro +++ b/src/components/upgrade-instructions/LinuxUpgradeSteps.astro @@ -1,6 +1,6 @@ --- import { Code } from '@astrojs/starlight/components'; -import { UPGRADE_VERSIONS, getFamilySlug } from '~/models/upgrade-instructions'; +import { UPGRADE_VERSIONS, LATEST_PATCH_VERSIONS, getFamilySlug } from '~/models/upgrade-instructions'; import type { Products } from '~/models/site.models'; import PrepareSection from './PrepareSection.astro'; @@ -58,8 +58,8 @@ function needsCompatibilityCheck(version: string): boolean { {versions.map((v) => { const newer = isNewerVersionScheme(v.version); const needsCompat = needsCompatibilityCheck(v.version); - const heading = v.patch && v.baseVersion !== v.displayVersion - ? `Upgrading ThingsBoard${isPE ? ' PE' : ''} to latest ${v.baseVersion} (${v.displayVersion})` + const heading = v.patch && v.baseVersion !== v.displayVersion && LATEST_PATCH_VERSIONS.has(v.version) + ? `Upgrading ThingsBoard${isPE ? ' PE' : ''} to ${v.displayVersion} (latest ${v.baseVersion} patch)` : `Upgrading ThingsBoard${isPE ? ' PE' : ''} to ${v.displayVersion}`; const prevLabel = v.x ? v.upgradableFrom : v.upgradableFrom; const prevDisplayLabel = isPE ? `${prevLabel} PE` : prevLabel; @@ -125,7 +125,7 @@ function needsCompatibilityCheck(version: string): boolean { {prevDisplayLabel} ) : ( prevDisplayLabel - )}{v.patch ? <>{' '}or any {v.baseVersion} patch : ''}.{' '} + )}{v.patch ? <>{' '}or any {v.patchableFrom ?? v.baseVersion} patch : ''}.{' '} In order to upgrade to {isPE ? `${v.displayVersion}PE` : v.displayVersion} you need to{' '} {prevHref ? ( <>upgrade to {prevDisplayLabel} first. @@ -199,7 +199,7 @@ function needsCompatibilityCheck(version: string): boolean {
{v.patch ? ( -

If you are upgrading from {v.upgradableFrom}, you must run the script below. However, if you are upgrading from version {`${v.baseVersion}.x`}, DO NOT run the upgrade script; proceed directly to starting the service.

+

If you are upgrading from {v.upgradableFrom}, you must run the script below. However, if you are upgrading from version {v.patchableFrom ?? v.baseVersion + '.x'}, DO NOT run the upgrade script; proceed directly to starting the service.

) : (

If you are upgrading from version {v.upgradableFrom}, you must run the script below.

)} diff --git a/src/components/upgrade-instructions/WindowsUpgradeSteps.astro b/src/components/upgrade-instructions/WindowsUpgradeSteps.astro index 4e24a49e47..92ddcba106 100644 --- a/src/components/upgrade-instructions/WindowsUpgradeSteps.astro +++ b/src/components/upgrade-instructions/WindowsUpgradeSteps.astro @@ -1,6 +1,6 @@ --- import { Code } from '@astrojs/starlight/components'; -import { UPGRADE_VERSIONS, getFamilySlug } from '~/models/upgrade-instructions'; +import { UPGRADE_VERSIONS, LATEST_PATCH_VERSIONS, getFamilySlug } from '~/models/upgrade-instructions'; import type { Products } from '~/models/site.models'; interface Props { @@ -47,8 +47,8 @@ function needsCompatibilityCheck(version: string): boolean { {versions.map((v) => { const newer = isNewerVersionScheme(v.version); const needsCompat = needsCompatibilityCheck(v.version); - const heading = v.patch && v.baseVersion !== v.displayVersion - ? `Upgrading ThingsBoard${isPE ? ' PE' : ''} to latest ${v.baseVersion} (${v.displayVersion})` + const heading = v.patch && v.baseVersion !== v.displayVersion && LATEST_PATCH_VERSIONS.has(v.version) + ? `Upgrading ThingsBoard${isPE ? ' PE' : ''} to ${v.displayVersion} (latest ${v.baseVersion} patch)` : `Upgrading ThingsBoard${isPE ? ' PE' : ''} to ${v.displayVersion}`; const prevLabel = v.x ? v.upgradableFrom : v.upgradableFrom; const prevDisplayLabel = isPE ? `${prevLabel} PE` : prevLabel; @@ -114,7 +114,7 @@ function needsCompatibilityCheck(version: string): boolean { {prevHref ? ( {prevDisplayLabel} ) : prevDisplayLabel} - {v.patch && <>{' '}or any {v.baseVersion} patch}.{' '} + {v.patch && <>{' '}or any {v.patchableFrom ?? v.baseVersion} patch}.{' '} In order to upgrade to {isPE ? `${v.displayVersion}PE` : v.displayVersion} you need to{' '} {prevHref ? ( <>upgrade to {prevDisplayLabel} first. @@ -183,7 +183,7 @@ function needsCompatibilityCheck(version: string): boolean {
{v.patch ? ( -

If you are upgrading from {v.upgradableFrom}, you must run the script below. However, if you are upgrading from version {`${v.baseVersion}.x`}, DO NOT run the upgrade script; proceed directly to starting the service.

+

If you are upgrading from {v.upgradableFrom}, you must run the script below. However, if you are upgrading from version {v.patchableFrom ?? v.baseVersion + '.x'}, DO NOT run the upgrade script; proceed directly to starting the service.

) : (

If you are upgrading from version {v.upgradableFrom}, you must run the script below.

)} diff --git a/src/models/upgrade-instructions.ts b/src/models/upgrade-instructions.ts index 1fcd29dff2..0cfbe24a12 100644 --- a/src/models/upgrade-instructions.ts +++ b/src/models/upgrade-instructions.ts @@ -13,6 +13,8 @@ export interface UpgradeVersion { releaseDatePe?: string; /** "upgradable-from" value, e.g. "4.2.1.x" or "4.1.0" */ upgradableFrom: string; + /** Optional override for the in-family patch label used in "or any X patch" text and the "from version X" upgrade-script note. When unset, templates fall back to baseVersion (and baseVersion.x for the script note). */ + patchableFrom?: string; /** Anchor of the upgradable-from version on the same platform page */ prevVersionAnchor?: string; lts: boolean; @@ -39,6 +41,17 @@ export function getFamilySlug(family: string): string { return 'v' + family.replace(/\./g, '-') + '-x'; } +/** + * All upgrade-eligible versions, newest-first. + * + * ORDERING IS LOAD-BEARING — keep entries in descending version order, and within + * a baseVersion group list the newest patch first. Consumers rely on array position: + * - UPGRADE_FAMILIES dedups by position to derive family order + * - UpgradeTable / *UpgradeSteps render rows in array order + * - LATEST_PATCH_VERSIONS treats the first entry per baseVersion as the latest + * An out-of-order insert silently mislabels "(latest patch)" and visibly reorders + * the upgrade table and steps. + */ export const UPGRADE_VERSIONS: UpgradeVersion[] = [ { version: '4.3.1.2', @@ -47,6 +60,7 @@ export const UPGRADE_VERSIONS: UpgradeVersion[] = [ baseVersion: '4.3.1', releaseDate: 'May 28 2026', upgradableFrom: '4.2.1.x', + patchableFrom: '4.3.x', prevVersionAnchor: 'v4-3-0-1', lts: true, patch: true, @@ -63,6 +77,7 @@ export const UPGRADE_VERSIONS: UpgradeVersion[] = [ baseVersion: '4.3.1', releaseDate: 'Mar 31 2026', upgradableFrom: '4.2.1.x', + patchableFrom: '4.3.x', prevVersionAnchor: 'v4-3-0-1', lts: true, patch: true, @@ -80,6 +95,7 @@ export const UPGRADE_VERSIONS: UpgradeVersion[] = [ baseVersion: '4.3.1', releaseDate: 'Mar 10 2026', upgradableFrom: '4.2.1.x', + patchableFrom: '4.3.x', prevVersionAnchor: 'v4-3-0-1', lts: true, patch: true, @@ -708,3 +724,21 @@ export const UPGRADE_VERSIONS: UpgradeVersion[] = [ /** Unique version families in order, e.g. ["4.3", "4.2", "4.1", ...] */ export const UPGRADE_FAMILIES: string[] = [...new Set(UPGRADE_VERSIONS.map((v) => v.family))]; + +/** + * Version strings that are the newest patch within their baseVersion family. + * Relies on UPGRADE_VERSIONS being newest-first per baseVersion (see note there) — + * the first entry seen for each baseVersion is taken as the latest. + */ +export const LATEST_PATCH_VERSIONS: Set = (() => { + const seen = new Set(); + const latest = new Set(); + for (const v of UPGRADE_VERSIONS) { + if (!v.patch || !v.baseVersion) continue; + if (!seen.has(v.baseVersion)) { + seen.add(v.baseVersion); + latest.add(v.version); + } + } + return latest; +})(); diff --git a/src/pages/docs/installation/upgrade-instructions/[platform]/[familySlug].astro b/src/pages/docs/installation/upgrade-instructions/[platform]/[familySlug].astro index 34ce14f567..5b0aa00841 100644 --- a/src/pages/docs/installation/upgrade-instructions/[platform]/[familySlug].astro +++ b/src/pages/docs/installation/upgrade-instructions/[platform]/[familySlug].astro @@ -1,6 +1,6 @@ --- import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro'; -import { UPGRADE_VERSIONS, UPGRADE_FAMILIES, getFamilySlug } from '~/models/upgrade-instructions'; +import { UPGRADE_VERSIONS, UPGRADE_FAMILIES, LATEST_PATCH_VERSIONS, getFamilySlug } from '~/models/upgrade-instructions'; import { Products } from '~/models/site.models'; import LinuxUpgradeSteps from '~/components/upgrade-instructions/LinuxUpgradeSteps.astro'; import WindowsUpgradeSteps from '~/components/upgrade-instructions/WindowsUpgradeSteps.astro'; @@ -42,8 +42,8 @@ const familyVersions = UPGRADE_VERSIONS.filter((v) => v.family === family); const headings = familyVersions.map((v) => ({ depth: 2, slug: v.anchor, - text: v.patch - ? `Upgrading ThingsBoard to latest ${v.baseVersion} (${v.displayVersion})` + text: v.patch && v.baseVersion !== v.displayVersion && LATEST_PATCH_VERSIONS.has(v.version) + ? `Upgrading ThingsBoard to ${v.displayVersion} (latest ${v.baseVersion} patch)` : `Upgrading ThingsBoard to ${v.displayVersion}`, })); diff --git a/src/pages/docs/pe/installation/upgrade-instructions/[platform]/[familySlug].astro b/src/pages/docs/pe/installation/upgrade-instructions/[platform]/[familySlug].astro index 77dddf6334..53c66ee6d5 100644 --- a/src/pages/docs/pe/installation/upgrade-instructions/[platform]/[familySlug].astro +++ b/src/pages/docs/pe/installation/upgrade-instructions/[platform]/[familySlug].astro @@ -1,6 +1,6 @@ --- import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro'; -import { UPGRADE_VERSIONS, UPGRADE_FAMILIES, getFamilySlug } from '~/models/upgrade-instructions'; +import { UPGRADE_VERSIONS, UPGRADE_FAMILIES, LATEST_PATCH_VERSIONS, getFamilySlug } from '~/models/upgrade-instructions'; import { Products } from '~/models/site.models'; import LinuxUpgradeSteps from '~/components/upgrade-instructions/LinuxUpgradeSteps.astro'; import WindowsUpgradeSteps from '~/components/upgrade-instructions/WindowsUpgradeSteps.astro'; @@ -42,8 +42,8 @@ const familyVersions = UPGRADE_VERSIONS.filter((v) => v.family === family); const headings = familyVersions.map((v) => ({ depth: 2, slug: v.anchor, - text: v.patch - ? `Upgrading ThingsBoard PE to latest ${v.baseVersion} (${v.displayVersion})` + text: v.patch && v.baseVersion !== v.displayVersion && LATEST_PATCH_VERSIONS.has(v.version) + ? `Upgrading ThingsBoard PE to ${v.displayVersion} (latest ${v.baseVersion} patch)` : `Upgrading ThingsBoard PE to ${v.displayVersion}`, }));