@@ -207,6 +207,53 @@ def get_build_version(version_type: str, build_type: str) -> str:
207207 return build_version
208208
209209
210+ def validate_specified_version (build_type : str , version : str ) -> str :
211+ """Validate a manually specified version and return it stripped.
212+
213+ Returns "" when no version is specified (blank/whitespace), signalling that the
214+ version should be computed automatically. Raises ``ValueError`` when a version is
215+ specified but is malformed or disagrees with ``build_type``.
216+
217+ This performs only local checks (no network), so it is safe to run as an early
218+ fail-fast step before any package-index access.
219+
220+ :param build_type: Build type ("stable"/"dev"/"rc"). Determines which pre-release
221+ suffix a specified version must carry.
222+ :param version: Candidate version string, or "" to compute automatically.
223+ :return: The stripped version, or "" if none was specified.
224+ :rtype: str
225+ """
226+ specified_version = (version or "" ).strip ()
227+ if not specified_version :
228+ return ""
229+
230+ if not VERSION_INPUT_RE .match (specified_version ):
231+ raise ValueError (
232+ f"Version \" { specified_version } \" is not a valid version. Expected "
233+ f"\" major.minor.patch\" optionally followed by \" .devN\" or \" .rcN\" ."
234+ )
235+
236+ # The specified version must match the selected build type, so a build tagged
237+ # "dev"/"rc" can't ship a version that lacks (or mismatches) the suffix.
238+ if build_type == "dev" and ".dev" not in specified_version :
239+ raise ValueError (
240+ f"Build type \" dev\" requires a \" .devN\" version, but got "
241+ f"\" { specified_version } \" ."
242+ )
243+ if build_type == "rc" and ".rc" not in specified_version :
244+ raise ValueError (
245+ f"Build type \" rc\" requires a \" .rcN\" version, but got "
246+ f"\" { specified_version } \" ."
247+ )
248+ if build_type == "stable" and (".dev" in specified_version or ".rc" in specified_version ):
249+ raise ValueError (
250+ f"Build type \" stable\" requires a \" major.minor.patch\" version "
251+ f"without a pre-release suffix, but got \" { specified_version } \" ."
252+ )
253+
254+ return specified_version
255+
256+
210257def resolve_build_version (version_type : str , build_type : str , version : str = "" ) -> str :
211258 """Resolve the version to ship for this run.
212259
@@ -226,39 +273,24 @@ def resolve_build_version(version_type: str, build_type: str, version: str = "")
226273 :return: The version to ship.
227274 :rtype: str
228275 """
229- specified_version = ( version or "" ). strip ( )
276+ specified_version = validate_specified_version ( build_type , version )
230277 if specified_version :
231- if not VERSION_INPUT_RE .match (specified_version ):
232- raise ValueError (
233- f"Version \" { specified_version } \" is not a valid version. Expected "
234- f"\" major.minor.patch\" optionally followed by \" .devN\" or \" .rcN\" ."
235- )
236-
237- # The specified version must match the selected build type, so a build tagged
238- # "dev"/"rc" can't ship a version that lacks (or mismatches) the suffix.
239- if build_type == "dev" and ".dev" not in specified_version :
240- raise ValueError (
241- f"Build type \" dev\" requires a \" .devN\" version, but got "
242- f"\" { specified_version } \" ."
243- )
244- if build_type == "rc" and ".rc" not in specified_version :
245- raise ValueError (
246- f"Build type \" rc\" requires a \" .rcN\" version, but got "
247- f"\" { specified_version } \" ."
248- )
249- if build_type == "stable" and (".dev" in specified_version or ".rc" in specified_version ):
250- raise ValueError (
251- f"Build type \" stable\" requires a \" major.minor.patch\" version "
252- f"without a pre-release suffix, but got \" { specified_version } \" ."
253- )
254-
255278 print (f"Using manually specified version: { specified_version } " )
256279 return specified_version
257280
258281 return get_build_version (version_type , build_type )
259282
260283
261284if __name__ == "__main__" :
285+ import sys
286+
287+ # Early fail-fast mode: validate the manually specified version (if any) without
288+ # touching the network, so a bad input stops the run before expensive setup.
289+ if "--validate-only" in sys .argv :
290+ validate_specified_version (BUILD_TYPE , VERSION )
291+ print ("Version input is valid." )
292+ sys .exit (0 )
293+
262294 build_version = resolve_build_version (RELEASE_TYPE , BUILD_TYPE , VERSION )
263295
264296 print (f"Package version: { build_version } " )
0 commit comments