Skip to content

macOS minor update not selected when BSI/RSR suffix variant present (e.g. 26.3(a) installed, 26.4 available) #369

@uurazzle

Description

@uurazzle

Summary

When a Mac is running a macOS version with a BSI/RSR suffix (e.g. 26.3(a)) and a standard minor update is available (e.g. 26.4), super fails to select the minor update as the install target. macos_msu_minor_update_target is reported as FALSE.

Environment

  • super version tested: 5.1rc4
  • Likely affected versions: Earlier releases that share the same macos_msu_array selection loop — not believed to be a regression introduced in 5.1rc4
  • macOS installed: 26.3(a)
  • macOS available: 26.4

Steps to reproduce

sudo cp /Library/Management/super/logs/super.log /tmp/super_before.log
sudo /path/to/super/super --verbose-mode --test-mode
comm -13 /tmp/super_before.log /Library/Management/super/logs/super.log > /tmp/super_new.log
grep -ni "macos_msu_array\|macos_msu_minor_update\|msu_sanitized_list\|Target:" /tmp/super_new.log

Observed output

323:Mon Mar 30 12:10:59 mlpc-pt02-lt super[76818]: Verbose Mode: Function main: Line 10925: macos_msu_minor_update_target is: FALSE

Expected behavior

super should identify 26.4 as the minor update target and set macos_msu_minor_update_target to a valid update entry.

Root cause — current code (line 6486)

The loop iterates all same-major candidates and unconditionally overwrites macos_msu_minor_update on every matching pass. When the sorted array contains both 26.3(a) and 26.4, the (a) suffix causes the version major extraction (sed -e 's/\..*//g') to behave unexpectedly, and the last-write-wins logic can leave the wrong (or no) item selected.

# Current code — problematic
local macos_msu_array
macos_msu_array=($(echo "${msu_sanitized_list}" | grep 'macOS' | awk -F ',' '{print $1 "," $2 "," $4 "," $3}' | sort -t ':' -k5 -V -r))
local macos_msu_major_upgrade
macos_msu_major_upgrade="FALSE"
local macos_msu_minor_update
macos_msu_minor_update="FALSE"
for macos_msu_item in "${macos_msu_array[@]}"; do
    [[ "$(echo "${macos_msu_item}" | awk -F ',' '{print $4}' | sed -e 's/.*://g' -e 's/\..*//g')" != "${macos_version_major}" ]] && macos_msu_major_upgrade="${macos_msu_item}"
    [[ "$(echo "${macos_msu_item}" | awk -F ',' '{print $4}' | sed -e 's/.*://g' -e 's/\..*//g')" == "${macos_version_major}" ]] && macos_msu_minor_update="${macos_msu_item}"
done

Problems:

  • Both assignments run on every iteration — the last matching item always wins, regardless of version order.
  • Version strings like 26.3(a) are not stripped of their suffix before the major version comparison, making matches unreliable.
  • No awareness of --install-macos-bsi-updates when selecting between candidates.

Proposed fix

Replace the loop with deterministic selection: normalize numeric version components, skip BSI/RSR candidates when BSI updates are disabled, and prefer the highest numeric version with a BSI tie-break when enabled.

# Proposed fix
local macos_msu_array
macos_msu_array=($(echo "${msu_sanitized_list}" | grep 'macOS' | awk -F ',' '{print $1 "," $2 "," $4 "," $3}' | sort -t ':' -k5 -V -r))
local macos_msu_major_upgrade
macos_msu_major_upgrade="FALSE"
local macos_msu_minor_update
macos_msu_minor_update="FALSE"
local macos_msu_minor_update_best_normalized=0
local macos_msu_minor_update_best_is_bsi=0
for macos_msu_item in "${macos_msu_array[@]}"; do
    local macos_msu_item_version
    local macos_msu_item_major
    local macos_msu_item_is_bsi
    local macos_msu_item_version_base
    local macos_msu_item_version_normalized
    macos_msu_item_version=$(echo "${macos_msu_item}" | awk -F ',' '{print $4}' | sed -e 's/.*://g')
    macos_msu_item_major=$(echo "${macos_msu_item_version}" | sed -e 's/\..*//g')
    macos_msu_item_is_bsi=0
    [[ "${macos_msu_item_version}" == *"("* ]] && macos_msu_item_is_bsi=1

    # Keep first available major-upgrade candidate (array is already sorted highest-first).
    if [[ "${macos_msu_item_major}" != "${macos_version_major}" ]] && [[ "${macos_msu_major_upgrade}" == "FALSE" ]]; then
        macos_msu_major_upgrade="${macos_msu_item}"
        continue
    fi

    # Evaluate same-major candidates for minor updates.
    [[ "${macos_msu_item_major}" != "${macos_version_major}" ]] && continue

    # If BSI/RSR updates are disabled, skip candidates that include "(x)" suffixes.
    if [[ "${install_macos_bsi_updates}" != "TRUE" ]] && [[ ${macos_msu_item_is_bsi} -eq 1 ]]; then
        continue
    fi

    # Normalize only numeric version components for reliable comparisons.
    macos_msu_item_version_base=$(echo "${macos_msu_item_version}" | sed -e 's/[^0-9.].*//g')
    macos_msu_item_version_normalized=$(echo "${macos_msu_item_version_base}" | awk -F '.' '{printf "%02d%02d%02d", $1, $2, $3}')

    if [[ "${macos_msu_minor_update}" == "FALSE" ]] || [[ ${macos_msu_item_version_normalized} -gt ${macos_msu_minor_update_best_normalized} ]]; then
        macos_msu_minor_update="${macos_msu_item}"
        macos_msu_minor_update_best_normalized=${macos_msu_item_version_normalized}
        macos_msu_minor_update_best_is_bsi=${macos_msu_item_is_bsi}
    elif [[ ${macos_msu_item_version_normalized} -eq ${macos_msu_minor_update_best_normalized} ]] && [[ "${install_macos_bsi_updates}" == "TRUE" ]] && [[ ${macos_msu_item_is_bsi} -gt ${macos_msu_minor_update_best_is_bsi} ]]; then
        # Tie-break on same numeric version: prefer BSI variant when BSI updates are enabled.
        macos_msu_minor_update="${macos_msu_item}"
        macos_msu_minor_update_best_is_bsi=${macos_msu_item_is_bsi}
    fi
done

What this fixes:

  1. The highest same-major numeric version is always preferred — no more last-write-wins overwriting.
  2. BSI/RSR suffix versions (26.3(a)) are stripped before numeric comparison, making major version extraction reliable.
  3. When --install-macos-bsi-updates is not TRUE, candidates with (x) suffixes are skipped entirely.
  4. When the numeric version is equal, the BSI variant is preferred only if BSI updates are explicitly enabled.

This fix has been tested on multiple affected machines running 26.3(a) with 26.4 available and confirmed to resolve the issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions