From 810e63beaddccfe41bc338aa46cf2ce409606728 Mon Sep 17 00:00:00 2001 From: moizpgedge Date: Mon, 11 Aug 2025 19:49:29 +0500 Subject: [PATCH 1/4] minor version/build version issue resolved --- cli/scripts/util.py | 145 ++++++++++++-------------------------------- 1 file changed, 40 insertions(+), 105 deletions(-) diff --git a/cli/scripts/util.py b/cli/scripts/util.py index da2cf20e..e6f395cb 100644 --- a/cli/scripts/util.py +++ b/cli/scripts/util.py @@ -124,122 +124,57 @@ def get_default_spock(pgv): return(DEFAULT_SPOCK) - - def validate_spock_pg_compat(spock_ver: str = None, pg_ver: str = None) -> None: - """ - Compatibility rules: - • If Spock < 5.0.0 ⇒ works with any supported PostgreSQL major. - • If Spock ≥ 5.0.0 ⇒ - – PG15 must be ≥ 15.13 - – PG16 must be ≥ 16.9 - – PG17 must be ≥ 17.5 - - Also supports shorthand Spock strings: - – "50" → "5.0.0", "40" → "4.0.0", etc. - """ - # 0) Fill in defaults if user didn’t pass anything - if not pg_ver: - pg_ver = DEFAULT_PG - else: - pg_ver = str(pg_ver) # ← force to string - + # --- defaults (keep yours as-is) --- + pg_ver = str(pg_ver) if pg_ver else str(DEFAULT_PG) if not spock_ver: - maj = int(pg_ver.split(".", 1)[0]) + try: + maj = int(pg_ver.split(".", 1)[0]) + except Exception: + maj = 17 spock_ver = DEFAULT_SPOCK_17 if maj == 17 else DEFAULT_SPOCK - else: - spock_ver = str(spock_ver) # ← force to string - - - # 0.5) Normalize two-digit shorthand (e.g. "50" → "5.0.0") - m = re.fullmatch(r'(\d)(\d)$', spock_ver) - if m: - spock_ver = f"{int(m.group(1))}.{int(m.group(2))}.0" - - # 1) Parse Spock version (abort on bad format) - try: - spv = Version(spock_ver) - except ValueError: - exit_message(f"Invalid Spock version '{spock_ver}'. Aborting.", 1, isJSON) - - # 2) If Spock < 5 ⇒ compatible with any PG - if spv.major < 5: + spock_ver = str(spock_ver) + + # --- parse Spock major w/o 'packaging' dependency --- + m_sp = re.fullmatch(r'(\d)(\d)$', spock_ver) # "50" -> "5.0.0" + if m_sp: + spock_ver = f"{int(m_sp.group(1))}.{int(m_sp.group(2))}.0" + spock_major = int(spock_ver.split('.', 1)[0]) + if spock_major < 5: return - # — New block: handle pg_ver with “-1” or “-2” suffix - rev = None - rev_match = re.fullmatch(r'(\d+)\.(\d+)-(1|2)$', pg_ver) - if rev_match: - pg_major = int(rev_match.group(1)) - pg_patch = int(rev_match.group(2)) - rev = int(rev_match.group(3)) + # --- parse PG "MAJOR.MINOR-BUILD" (build 1 or 2 only) --- + m = re.fullmatch(r'(\d+)\.(\d+)-(1|2)$', pg_ver) + if not m: + exit_message(f"Invalid PostgreSQL version '{pg_ver}'. Use 'MAJOR.MINOR-BUILD' (e.g., 17.6-1).", 1, isJSON) + pg_major, pg_minor, pg_build = int(m.group(1)), int(m.group(2)), int(m.group(3)) + + # --- thresholds for 15/16/17: require -2 exactly at the min minor --- + thresholds = {15: (13, 2), 16: (9, 2), 17: (5, 2)} - # reject revision “-1” on Spock ≥5 - if rev == 1: + if pg_major in thresholds: + min_minor, min_build = thresholds[pg_major] + if pg_minor < min_minor: exit_message( - f"Error: PostgreSQL {pg_major}.{pg_patch}-1 is not supported with Spock {spv}; " - "please use the “-2” revision instead.", - 1, - isJSON + f"Error: Spock {spock_ver} requires PostgreSQL {pg_major}.{min_minor}-{min_build} or newer; " + f"you have {pg_major}.{pg_minor}-{pg_build}.", 1, isJSON ) - # for “-2”, we strip suffix and proceed with pg_major/pg_patch below - # end new block - - # 3) Spock ≥ 5 ⇒ enforce minimum‐patch for each PG major - minimum_patches = { - 15: 13, - 16: 9, - 17: 5, - } - - # 4) Extract PG major and patch (if not already set by rev_match) - if rev_match: - # pg_major, pg_patch are already set - pass - elif "." not in pg_ver: - # bare-major → use its minimum patch - try: - pg_major = int(pg_ver) - except ValueError: - exit_message(f"Invalid PostgreSQL version '{pg_ver}'. Aborting.", 1, isJSON) - if pg_major not in minimum_patches: - allowed = ", ".join(str(m) for m in minimum_patches) + if pg_minor == min_minor and pg_build < min_build: + # At threshold minor, only -2 allowed exit_message( - f"Error: Spock {spv} supports only PostgreSQL majors {allowed}; " - f"you have {pg_major}. Aborting.", - 1, - isJSON + f"Error: Spock {spock_ver} requires PostgreSQL {pg_major}.{min_minor}-2 at the minimum; " + f"you have {pg_major}.{pg_minor}-{pg_build}.", 1, isJSON ) - pg_patch = minimum_patches[pg_major] - else: - parts = pg_ver.split(".", 2) - if len(parts) < 2: - exit_message(f"Invalid PostgreSQL version '{pg_ver}'. Aborting.", 1, isJSON) - try: - pg_major = int(parts[0]) - pg_patch = int(parts[1]) - except ValueError: - exit_message(f"Invalid PostgreSQL version '{pg_ver}'. Aborting.", 1, isJSON) - - # 5) Major must be supported - if pg_major not in minimum_patches: - allowed = ", ".join(str(m) for m in minimum_patches) - exit_message( - f"Error: Spock {spv} supports only PostgreSQL majors {allowed}; " - f"you have {pg_major}. Aborting.", - 1, - isJSON - ) + # pg_minor > min_minor → allow -1 or -2 + return + + # --- future majors (≥18): allow -1 or -2, any minor --- + if pg_major >= 18: + return + + # --- unsupported majors (<15) --- + exit_message("Error: Supported PG majors are 15, 16, 17, and 18+.", 1, isJSON) - # 6) Enforce minimum‐patch - required = minimum_patches[pg_major] - if pg_patch < required: - exit_message( - f"Error: Spock {spv} requires PostgreSQL {pg_major}.{required} or newer; " - f"you have {pg_major}.{pg_patch}. Aborting.", - 1, - isJSON - ) def get_cpu_info(): try: import cpuinfo From 496146a8b1d2a40bbccefcae1187bb908d1fa1fe Mon Sep 17 00:00:00 2001 From: Matthew Mols Date: Tue, 12 Aug 2025 11:27:21 -0500 Subject: [PATCH 2/4] use semantic_version for more consistent compares --- cli/scripts/util.py | 51 +++++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 32 deletions(-) diff --git a/cli/scripts/util.py b/cli/scripts/util.py index e6f395cb..0513f697 100644 --- a/cli/scripts/util.py +++ b/cli/scripts/util.py @@ -139,41 +139,28 @@ def validate_spock_pg_compat(spock_ver: str = None, pg_ver: str = None) -> None: m_sp = re.fullmatch(r'(\d)(\d)$', spock_ver) # "50" -> "5.0.0" if m_sp: spock_ver = f"{int(m_sp.group(1))}.{int(m_sp.group(2))}.0" - spock_major = int(spock_ver.split('.', 1)[0]) - if spock_major < 5: - return - # --- parse PG "MAJOR.MINOR-BUILD" (build 1 or 2 only) --- - m = re.fullmatch(r'(\d+)\.(\d+)-(1|2)$', pg_ver) - if not m: - exit_message(f"Invalid PostgreSQL version '{pg_ver}'. Use 'MAJOR.MINOR-BUILD' (e.g., 17.6-1).", 1, isJSON) - pg_major, pg_minor, pg_build = int(m.group(1)), int(m.group(2)), int(m.group(3)) - - # --- thresholds for 15/16/17: require -2 exactly at the min minor --- - thresholds = {15: (13, 2), 16: (9, 2), 17: (5, 2)} - - if pg_major in thresholds: - min_minor, min_build = thresholds[pg_major] - if pg_minor < min_minor: - exit_message( - f"Error: Spock {spock_ver} requires PostgreSQL {pg_major}.{min_minor}-{min_build} or newer; " - f"you have {pg_major}.{pg_minor}-{pg_build}.", 1, isJSON - ) - if pg_minor == min_minor and pg_build < min_build: - # At threshold minor, only -2 allowed - exit_message( - f"Error: Spock {spock_ver} requires PostgreSQL {pg_major}.{min_minor}-2 at the minimum; " - f"you have {pg_major}.{pg_minor}-{pg_build}.", 1, isJSON - ) - # pg_minor > min_minor → allow -1 or -2 - return + spock_version_obj = Version.coerce(spock_ver) - # --- future majors (≥18): allow -1 or -2, any minor --- - if pg_major >= 18: + if spock_version_obj.major < 5: return - - # --- unsupported majors (<15) --- - exit_message("Error: Supported PG majors are 15, 16, 17, and 18+.", 1, isJSON) + + pg_version_obj = Version.coerce(pg_ver) + + if not pg_version_obj.minor: + return + + min_version_thresholds = {15: Version.coerce("15.13-2"), 16: Version.coerce("16.9-2"), 17: Version.coerce("17.5-2")} + + if pg_version_obj.major in min_version_thresholds: + min_version_obj = min_version_thresholds[pg_version_obj.major] + min_version_build = min_version_obj.prerelease[0] if min_version_obj.prerelease else "0" + if pg_version_obj < min_version_obj: + exit_message( + f"Spock {spock_ver} requires PostgreSQL {pg_version_obj.major} >= {min_version_obj.major}.{min_version_obj.minor}-{min_version_build}. You provided {pg_ver}.", + 1, + isJSON, + ) def get_cpu_info(): try: From 1bb834ec104aa1ec8c01f881adc48fb2a111803c Mon Sep 17 00:00:00 2001 From: Matthew Mols Date: Tue, 12 Aug 2025 11:41:25 -0500 Subject: [PATCH 3/4] improve error handling --- cli/scripts/util.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/cli/scripts/util.py b/cli/scripts/util.py index 0513f697..c95915b4 100644 --- a/cli/scripts/util.py +++ b/cli/scripts/util.py @@ -140,13 +140,19 @@ def validate_spock_pg_compat(spock_ver: str = None, pg_ver: str = None) -> None: if m_sp: spock_ver = f"{int(m_sp.group(1))}.{int(m_sp.group(2))}.0" - spock_version_obj = Version.coerce(spock_ver) + try: + spock_version_obj = Version.coerce(spock_ver) + except Exception as e: + exit_message(f"Invalid Spock version '{spock_ver}'.", 1, isJSON) if spock_version_obj.major < 5: return - pg_version_obj = Version.coerce(pg_ver) - + try: + pg_version_obj = Version.coerce(pg_ver) + except Exception as e: + exit_message(f"Invalid PostgreSQL version '{pg_ver}'.", 1, isJSON) + if not pg_version_obj.minor: return From aeeecf4da0e2560aa66ce0aadb9c5a851c5215b9 Mon Sep 17 00:00:00 2001 From: Matthew Mols Date: Tue, 12 Aug 2025 15:16:57 -0500 Subject: [PATCH 4/4] pass in spock component during upgrade check --- cli/scripts/um.py | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/cli/scripts/um.py b/cli/scripts/um.py index ceff8e3a..8c359b6a 100755 --- a/cli/scripts/um.py +++ b/cli/scripts/um.py @@ -95,7 +95,7 @@ def install(component, active=True): # Trigger pre-check ONLY for Spock 5.0 artifacts if _SPOCK50_RE.match(component): - validate_spock_upgrade() # should sys.exit(1) on failure; otherwise just returns + validate_spock_upgrade(component) # should sys.exit(1) on failure; otherwise just returns # Common path (no duplication) if active not in (True, False): @@ -244,7 +244,7 @@ def verify_metadata(Project="", Stage="prod", IsCurrent=0): meta.pretty_sql(sql) -def validate_spock_upgrade(): +def validate_spock_upgrade(spock_component): """ Validate Spock↔PostgreSQL compatibility for an upcoming Spock 5 install. """ @@ -282,37 +282,38 @@ def validate_spock_upgrade(): if not pg_row: sys.exit("ERROR: No PostgreSQL version row found.") - pg_ver = pg_row[0] - spock_ver, spock_comp = (sp_row or (None, None)) + existing_pg_ver = pg_row[0] + existing_spock_ver, existing_spock_comp = (sp_row or (None, None)) # Already on Spock 5? No-op. - if spock_ver and ( - _SPOCK5_NAME_RE.match(spock_comp) or _SPOCK5_VER_RE.match(spock_ver) + if existing_spock_ver and ( + _SPOCK5_NAME_RE.match(existing_spock_comp) or _SPOCK5_VER_RE.match(existing_spock_ver) ): return 0 - + + # Trim "spock" off the front to get the version (e.g., "spock50" -> "50") + requested_spock_ver = spock_component + if requested_spock_ver and requested_spock_ver.lower().startswith("spock"): + requested_spock_ver = requested_spock_ver[5:] + # Downtime warning banner = "=" * 80 - if not spock_ver: - # Case 1: no existing Spock installed, run validation but don't print banner - try: - util.validate_spock_pg_compat('50', pg_ver) - except Exception as exc: - sys.exit(f"ERROR: Compatibility check failed: {exc}") - - elif spock_ver.startswith('4'): + if existing_spock_ver: # Case 2: Spock 4.x installed, this is a major upgrade print(f"\n{banner}") print("*** WARNING: This operation will cause downtime! ***") print(f"{banner}\n") - print(f"Detected Spock version {spock_ver} on PostgreSQL {pg_ver}") - try: - util.validate_spock_pg_compat(spock_ver, pg_ver) - except Exception as exc: - sys.exit(f"ERROR: Compatibility check failed: {exc}") + print(f"Detected existing Spock version {existing_spock_ver} on PostgreSQL {existing_pg_ver}") + try: + util.validate_spock_pg_compat(requested_spock_ver, existing_pg_ver) + except Exception as exc: + sys.exit(f"ERROR: Compatibility check failed: {exc}") + + if existing_spock_ver: print("Compatibility check passed.") + return 0 if __name__ == "__main__":