diff --git a/.claude/commands/reinstall.md b/.claude/commands/reinstall.md index 0e81c4a..259b151 100644 --- a/.claude/commands/reinstall.md +++ b/.claude/commands/reinstall.md @@ -1,15 +1,6 @@ -# Reinstall tensors - -Bump version, reinstall locally and on junkpile. - -Run the reinstall script: - +--- +description: Reinstall tensors - update version with git hash, install locally and on junkpile +--- ```bash -python scripts/reinstall.py +${CLAUDE_PROJECT_ROOT}/.claude/commands/reinstall.sh ``` - -This will: -1. Bump the patch version in pyproject.toml -2. Install locally with `uv pip install -e .` -3. Sync the project to junkpile via rsync -4. Install on junkpile with `pip install -e '.[server]'` diff --git a/.claude/commands/reinstall.sh b/.claude/commands/reinstall.sh new file mode 100755 index 0000000..758a4cb --- /dev/null +++ b/.claude/commands/reinstall.sh @@ -0,0 +1,73 @@ +#!/bin/bash +# Tensors Reinstall Script +# Updates version with git hash, reinstalls locally and on junkpile +set -euo pipefail + +TENSORS_ROOT="/Users/chi/Projects/tensors" +JUNKPILE_HOST="chi@junkpile" +JUNKPILE_PATH="/opt/tensors/app" + +cd "$TENSORS_ROOT" + +# Get current version from __init__.py +CURRENT_VERSION=$(sed -n 's/^__version__ = "\([^"]*\)"/\1/p' tensors/__init__.py) +BASE_VERSION=$(echo "$CURRENT_VERSION" | sed 's/+.*//') + +# Get the LAST commit hash BEFORE we make any changes +LAST_HASH=$(git rev-parse --short HEAD) + +# New version with hash +NEW_VERSION="${BASE_VERSION}+${LAST_HASH}" + +echo "=== Tensors Reinstall ===" +echo "Current version: $CURRENT_VERSION" +echo "Base version: $BASE_VERSION" +echo "Last commit: $LAST_HASH" +echo "New version: $NEW_VERSION" +echo "" + +# Check if version already matches (avoid loop) +if [[ "$CURRENT_VERSION" == "$NEW_VERSION" ]]; then + echo "[1/5] Version already at $NEW_VERSION, skipping version bump" +else + echo "[1/5] Updating version to $NEW_VERSION..." + sed -i '' "s/__version__ = \".*\"/__version__ = \"$NEW_VERSION\"/" tensors/__init__.py + echo " Updated tensors/__init__.py" + + # Commit version change if there are changes + if ! git diff --quiet; then + echo "" + echo "[2/5] Committing version update..." + git add tensors/__init__.py + git commit -m "Version $NEW_VERSION" + echo " Committed." + fi +fi + +# Push if ahead of remote +if git status | grep -q "Your branch is ahead"; then + echo "" + echo "Pushing to remote..." + git push +fi + +echo "" +echo "[3/5] Installing locally via uv..." +uv pip install -e . + +echo "" +echo "[4/5] Pulling on junkpile..." +ssh "$JUNKPILE_HOST" "cd $JUNKPILE_PATH && sudo -u tensors git checkout . && sudo -u tensors git pull" + +echo "" +echo "[5/5] Installing on junkpile..." +ssh "$JUNKPILE_HOST" "cd $JUNKPILE_PATH && sudo -u tensors uv sync && sudo systemctl restart tensors" + +echo "" +echo "=== Verification ===" +uv run tsr --version + +echo "" +echo "=== Done ===" +echo "Version: $NEW_VERSION" +echo "Installed locally and on junkpile." diff --git a/pyproject.toml b/pyproject.toml index b97a83f..ee705b3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,9 +1,9 @@ [project] name = "tensors" -version = "0.1.18" +dynamic = ["version"] description = "Read safetensor metadata and fetch CivitAI model information" readme = "README.md" -requires-python = ">=3.14" +requires-python = ">=3.12" dependencies = [ "safetensors>=0.4.0", "httpx>=0.27.0", @@ -24,6 +24,9 @@ tsr = "tensors:main" requires = ["hatchling"] build-backend = "hatchling.build" +[tool.hatch.version] +path = "tensors/__init__.py" + [tool.hatch.build.targets.wheel] packages = ["tensors"] diff --git a/scripts/reinstall.py b/scripts/reinstall.py index 7c6b26f..2caa0f8 100755 --- a/scripts/reinstall.py +++ b/scripts/reinstall.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Reinstall tensors locally and on junkpile.""" +"""Reinstall tensors locally and on junkpile with hash-suffixed versioning.""" from __future__ import annotations @@ -9,51 +9,81 @@ import sys from pathlib import Path PROJECT_ROOT = Path(__file__).parent.parent -PYPROJECT = PROJECT_ROOT / "pyproject.toml" +INIT_FILE = PROJECT_ROOT / "tensors" / "__init__.py" JUNKPILE_HOST = "chi@junkpile" -JUNKPILE_PATH = "~/Projects/tensors" +JUNKPILE_PATH = "/opt/tensors" def get_version() -> str: - """Get current version from pyproject.toml.""" - content = PYPROJECT.read_text() - match = re.search(r'version\s*=\s*"([^"]+)"', content) + """Get current version from __init__.py.""" + content = INIT_FILE.read_text() + match = re.search(r'__version__\s*=\s*"([^"]+)"', content) if not match: - raise ValueError("Could not find version in pyproject.toml") + raise ValueError("Could not find __version__ in tensors/__init__.py") return match.group(1) -def bump_version(current: str) -> str: - """Bump patch version.""" - parts = current.split(".") - parts[-1] = str(int(parts[-1]) + 1) - return ".".join(parts) +def get_base_version(version: str) -> str: + """Strip any +hash suffix from version.""" + return version.split("+")[0] + + +def get_git_hash() -> str: + """Get short git hash of HEAD.""" + result = subprocess.run( + ["git", "rev-parse", "--short", "HEAD"], + capture_output=True, + text=True, + check=True, + cwd=PROJECT_ROOT, + ) + return result.stdout.strip() def set_version(new_version: str) -> None: - """Update version in pyproject.toml.""" - content = PYPROJECT.read_text() - # Only replace the project version (line starts with 'version') - content = re.sub(r'^version\s*=\s*"[^"]+"', f'version = "{new_version}"', content, count=1, flags=re.MULTILINE) - PYPROJECT.write_text(content) - print(f" Updated pyproject.toml to {new_version}") + """Update version in __init__.py.""" + content = INIT_FILE.read_text() + content = re.sub(r'__version__\s*=\s*"[^"]+"', f'__version__ = "{new_version}"', content) + INIT_FILE.write_text(content) + print(f" Updated tensors/__init__.py to {new_version}") -def run(cmd: list[str], *, check: bool = True, capture: bool = False) -> subprocess.CompletedProcess[str]: +def run(cmd: list[str], *, check: bool = True, capture: bool = False, cwd: Path | None = None) -> subprocess.CompletedProcess[str]: """Run a command.""" print(f" $ {' '.join(cmd)}") - return subprocess.run(cmd, check=check, capture_output=capture, text=True) + return subprocess.run(cmd, check=check, capture_output=capture, text=True, cwd=cwd or PROJECT_ROOT) + + +def git_commit_version(version: str) -> bool: + """Commit version change if there are changes.""" + # Check if there are changes + result = run(["git", "diff", "--quiet", "tensors/__init__.py"], check=False, capture=True) + if result.returncode == 0: + return False # No changes + + run(["git", "add", "tensors/__init__.py"]) + run(["git", "commit", "-m", f"Version {version}"]) + return True + + +def git_push_if_ahead() -> bool: + """Push to remote if ahead.""" + result = run(["git", "status", "--porcelain", "-b"], capture=True) + if "ahead" in result.stdout: + run(["git", "push"]) + return True + return False def install_local() -> None: """Install locally with uv.""" - print("\n[2/4] Installing locally...") + print("\n[3/5] Installing locally...") run(["uv", "pip", "install", "-e", "."], check=True) def sync_to_junkpile() -> None: """Sync project to junkpile.""" - print("\n[3/4] Syncing to junkpile...") + print("\n[4/5] Syncing to junkpile...") excludes = [ ".git", ".venv", @@ -76,17 +106,35 @@ def sync_to_junkpile() -> None: def install_junkpile() -> None: """Install on junkpile.""" - print("\n[4/4] Installing on junkpile...") + print("\n[5/5] Installing on junkpile...") run(["ssh", JUNKPILE_HOST, f"cd {JUNKPILE_PATH} && pip install -e '.[server]'"]) def main() -> int: """Main entry point.""" current = get_version() - new_version = bump_version(current) + base_version = get_base_version(current) + git_hash = get_git_hash() + new_version = f"{base_version}+{git_hash}" - print(f"\n[1/4] Bumping version {current} -> {new_version}...") - set_version(new_version) + print("\n=== Tensors Reinstall ===") + print(f" Current version: {current}") + print(f" Base version: {base_version}") + print(f" Git hash: {git_hash}") + print(f" New version: {new_version}") + + if current == new_version: + print("\n[1/5] Version already current, skipping update") + else: + print(f"\n[1/5] Updating version to {new_version}...") + set_version(new_version) + + print("\n[2/5] Committing version change...") + if git_commit_version(new_version): + print(" Committed.") + git_push_if_ahead() + else: + print(" No changes to commit.") try: install_local() @@ -96,6 +144,10 @@ def main() -> int: print(f"\nError: Command failed with exit code {e.returncode}") return 1 + # Verify installation + print("\n=== Verification ===") + run(["uv", "run", "tsr", "--version"]) + print(f"\n Done! tensors {new_version} installed locally and on junkpile") return 0