mirror of
https://github.com/Donkie/Spoolman.git
synced 2026-10-22 10:02:17 -05:00
The bump script has only ever touched client/package.json, so client_v2 has sat at 0.0.1 since it was created. Nothing reads it -- the footer takes the version from GET /info -- but both clients ship in every release, and a package that claims 0.0.1 while the release says 0.25.0 is confusing to anyone who looks. The indent has to be per-client: each tree is formatted by its own prettier config and client_v2 uses tabs, so writing 2 spaces back would make `npm run lint` fail on the release commit itself. Also brings client_v2 up to the current version so the first bump after this is a normal increment rather than a jump from 0.0.1. Claude-Session: https://claude.ai/code/session_01UB5KocsTBm9GL5x3Uuup5F
146 lines
5.2 KiB
Python
146 lines
5.2 KiB
Python
"""A python script that bumps the version number of a project."""
|
|
|
|
# ruff: noqa: PLR2004, T201, S607
|
|
|
|
import json
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
def bump() -> None:
|
|
"""Bump the version number of the project."""
|
|
project_root = Path(__file__).parent.parent
|
|
|
|
if len(sys.argv) < 2:
|
|
print("Please specify a bump type, e.g. major, minor, micro.")
|
|
sys.exit(1)
|
|
|
|
if subprocess.run(["git", "diff", "--quiet", "pyproject.toml"], cwd=project_root, check=False).returncode != 0:
|
|
print("The pyproject.toml file is dirty, please commit your changes before bumping the version number.")
|
|
sys.exit(1)
|
|
|
|
if subprocess.run(["git", "diff", "--cached", "--quiet"], cwd=project_root, check=False).returncode != 0:
|
|
print("There are staged changes, please commit them before bumping the version number.")
|
|
sys.exit(1)
|
|
|
|
# Bump the version number by editing pyproject.toml directly.
|
|
bump_type = sys.argv[1]
|
|
|
|
new_version = _bump_pyproject(project_root, bump_type)
|
|
|
|
# Update the version number in both node projects. The indent differs because each
|
|
# client formats its own tree with its own prettier config (client_v2 uses tabs);
|
|
# writing the other one back would make `npm run lint` fail on a release commit.
|
|
for client_dir, indent in (("client", " "), ("client_v2", "\t")):
|
|
_update_node_pkg_version(project_root, client_dir, new_version, indent)
|
|
|
|
# Run uv lock to update the lock file
|
|
subprocess.run(["uv", "lock"], check=True)
|
|
|
|
# Regenerate requirements.txt from uv so bare-metal / Moonraker update_manager
|
|
# installs (which pip install -r requirements.txt) stay in sync with uv.
|
|
subprocess.run(
|
|
["uv", "export", "--no-dev", "-o", "requirements.txt", "--no-annotate", "--no-hashes"],
|
|
cwd=project_root,
|
|
check=True,
|
|
)
|
|
|
|
# Stage the changed files
|
|
subprocess.run(
|
|
[
|
|
"git",
|
|
"add",
|
|
"pyproject.toml",
|
|
"uv.lock",
|
|
"requirements.txt",
|
|
"client/package.json",
|
|
"client/package-lock.json",
|
|
"client_v2/package.json",
|
|
"client_v2/package-lock.json",
|
|
],
|
|
cwd=project_root,
|
|
check=True,
|
|
)
|
|
|
|
# Commit the changes
|
|
subprocess.run(["git", "commit", "-m", f"Bump version to {new_version}"], cwd=project_root, check=True)
|
|
|
|
# Tag the commit, prefix with "v"
|
|
subprocess.run(["git", "tag", f"v{new_version}"], cwd=project_root, check=True)
|
|
|
|
# Notify user that the process is complete
|
|
print(f"Bumped version to {new_version}.")
|
|
|
|
|
|
def _update_node_pkg_version(project_root: Path, client_dir: str, new_version: str, indent: str) -> None:
|
|
"""Set the version of one of the client packages and refresh its lock file.
|
|
|
|
Both clients ship in every release, so both track the Spoolman version even
|
|
though neither reads it at runtime (the footer takes the version from
|
|
``GET /info``). Keeping them in sync stops the packages from drifting into
|
|
something confusing to read.
|
|
"""
|
|
package_json = project_root / client_dir / "package.json"
|
|
with package_json.open("r") as f:
|
|
node_package = json.load(f)
|
|
node_package["version"] = new_version
|
|
with package_json.open("w") as f:
|
|
json.dump(node_package, f, indent=indent)
|
|
f.write("\n") # Ensure file ends with a newline, needed by prettier
|
|
|
|
# Run npm install to update the lock file with new version
|
|
# On windows, shell=True is required for npm to be found
|
|
if os.name == "nt":
|
|
subprocess.run(["npm", "install"], cwd=project_root / client_dir, check=True, shell=True) # noqa: S602
|
|
else:
|
|
subprocess.run(["npm", "install"], cwd=project_root / client_dir, check=True)
|
|
|
|
|
|
def _bump_pyproject(project_root: Path, bump_type: str) -> str:
|
|
"""Bump the version number in pyproject.toml. Returns the new version."""
|
|
pyproject_path = project_root.joinpath("pyproject.toml")
|
|
try:
|
|
pyproject_text = pyproject_path.read_text()
|
|
except OSError:
|
|
print("Failed to read pyproject.toml to determine current version.")
|
|
sys.exit(1)
|
|
|
|
# Parse current version expecting MAJOR.MINOR.PATCH (digits only)
|
|
version_match = re.search(
|
|
r'^\s*version\s*=\s*"(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)"',
|
|
pyproject_text,
|
|
re.MULTILINE,
|
|
)
|
|
if version_match is None:
|
|
print("Failed to parse current version from pyproject.toml.")
|
|
sys.exit(1)
|
|
|
|
major = int(version_match.group("major"))
|
|
minor = int(version_match.group("minor"))
|
|
patch = int(version_match.group("patch"))
|
|
|
|
if bump_type == "major":
|
|
major += 1
|
|
minor = 0
|
|
patch = 0
|
|
elif bump_type == "minor":
|
|
minor += 1
|
|
patch = 0
|
|
elif bump_type in ("micro", "patch"):
|
|
patch += 1
|
|
else:
|
|
print("Unknown bump type. Use 'major', 'minor' or 'micro'.")
|
|
sys.exit(1)
|
|
|
|
new_version = f"{major}.{minor}.{patch}"
|
|
|
|
# Replace only the first occurrence of the version line in pyproject.toml
|
|
pattern = re.compile(r"^\s*version\s*=.*$", re.MULTILINE)
|
|
new_line = f'version = "{new_version}"'
|
|
new_pyproject_text = pattern.sub(new_line, pyproject_text, count=1)
|
|
pyproject_path.write_text(new_pyproject_text)
|
|
return new_version
|