From 0cec86b0be84b6cdd5d0056f5fd60690592550ac Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Thu, 7 Aug 2025 20:26:55 +0000 Subject: [PATCH 1/7] feat: implement semantic versioning with setuptools-scm - Configure setuptools-scm in pyproject.toml for dynamic versioning - Add /api/version endpoint in Flask backend - Create useVersion hook and display version in frontend sidebar - Update README files with tagging workflow documentation - Remove hardcoded version in favor of Git tag-based versioning Resolves #175 Co-authored-by: yangm2 --- README.md | 34 +++++++++ backend/README.md | 64 ++++++++++++++++ backend/pyproject.toml | 9 ++- backend/tenantfirstaid/app.py | 15 ++++ frontend/README.md | 73 +++++++++++++++++++ frontend/src/hooks/useVersion.tsx | 35 +++++++++ frontend/src/pages/Chat/components/Navbar.tsx | 15 +++- 7 files changed, 242 insertions(+), 3 deletions(-) create mode 100644 backend/README.md create mode 100644 frontend/README.md create mode 100644 frontend/src/hooks/useVersion.tsx diff --git a/README.md b/README.md index bf3ed91..f9702d4 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,40 @@ Live at https://tenantfirstaid.com/ ``` `--keep-going` will continue to run checks, even if previous `make` rule fail. Omit if you want to stop after the first `make` rule fails. +## Versioning and Releases + +This project uses [semantic versioning](https://semver.org/) with automated version management via `setuptools-scm`. The version is automatically derived from Git tags. + +### Creating a Release + +1. **Determine the version bump type:** + - **Patch release** (0.2.0 → 0.2.1): Bug fixes, minor improvements + - **Minor release** (0.2.0 → 0.3.0): New features, backward-compatible changes + - **Major release** (0.2.0 → 1.0.0): Breaking changes + +2. **Create and push a tag:** + ```bash + # For a patch release (default for regular PRs) + git tag v0.2.1 + git push origin v0.2.1 + + # For a minor release (feature additions) + git tag v0.3.0 + git push origin v0.3.0 + + # For a major release (breaking changes) + git tag v1.0.0 + git push origin v1.0.0 + ``` + +3. **The version will automatically be updated** in the backend API and displayed in the frontend UI. + +### Checking the Current Version + +- **Backend API**: Visit `/api/version` endpoint +- **Frontend UI**: Check the sidebar navigation (bottom) +- **Command line**: Run `uv run python -c "from importlib.metadata import version; print(version('tenant-first-aid'))"` + ## Contributing We currently have regular project meetups: https://www.meetup.com/codepdx/ . Also check out https://www.codepdx.org/ to find our Discord server. diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000..e06a26a --- /dev/null +++ b/backend/README.md @@ -0,0 +1,64 @@ +# Tenant First Aid Backend + +Flask backend API for the Tenant First Aid chatbot. + +## Development Setup + +See the main [README.md](../README.md) for full setup instructions. + +## API Endpoints + +- `/api/init` - Initialize chat session +- `/api/query` - Send chat message +- `/api/history` - Get chat history +- `/api/clear-session` - Clear current session +- `/api/citation` - Get citation information +- `/api/version` - Get application version + +## Version Management + +This backend uses `setuptools-scm` for automatic version management based on Git tags. The version is dynamically generated from the repository's tag history. + +### Checking Version + +```bash +# In the backend directory +uv run python -c "from importlib.metadata import version; print(version('tenant-first-aid'))" + +# Or via the API +curl http://localhost:5001/api/version +``` + +### Creating a New Release + +1. Ensure all changes are committed and pushed +2. Create a new tag following semantic versioning: + ```bash + git tag v0.3.0 # or appropriate version + git push origin v0.3.0 + ``` +3. The version will automatically be updated in the application + +## Development Commands + +From the `backend/` directory: + +```bash +# Install dependencies +uv sync + +# Run the server +uv run python -m tenantfirstaid.app + +# Run tests +uv run pytest + +# Format code +uv run ruff format + +# Lint code +uv run ruff check + +# Type check +uv run ty check +``` \ No newline at end of file diff --git a/backend/pyproject.toml b/backend/pyproject.toml index d4b063f..57b3f69 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "tenant-first-aid" -version = "0.2.0" +dynamic = ["version"] requires-python = ">=3.12" dependencies = [ "flask>=3.1.1", @@ -21,6 +21,7 @@ dependencies = [ "python-dotenv", "pandas>=2.3.0", "vertexai>=1.43.0", + "setuptools-scm>=8", ] [tool.setuptools.packages.find] @@ -28,9 +29,13 @@ where = ["."] exclude = ["data*"] [build-system] -requires = ["setuptools>=61"] +requires = ["setuptools>=61", "setuptools-scm>=8"] build-backend = "setuptools.build_meta" +[tool.setuptools_scm] +version_scheme = "guess-next-dev" +local_scheme = "no-local-version" + [dependency-groups] dev = [ "ipdb>=0.13.13", diff --git a/backend/tenantfirstaid/app.py b/backend/tenantfirstaid/app.py index b9cf5e7..e4aea86 100644 --- a/backend/tenantfirstaid/app.py +++ b/backend/tenantfirstaid/app.py @@ -7,6 +7,11 @@ import os import secrets +try: + from importlib.metadata import version +except ImportError: + from importlib_metadata import version + if Path(".env").exists(): from dotenv import load_dotenv @@ -89,6 +94,16 @@ def clear_session(): return jsonify({"success": True}) +@app.get("/api/version") +def get_version(): + try: + app_version = version("tenant-first-aid") + return jsonify({"version": app_version}) + except Exception as e: + # Fallback if setuptools-scm isn't working or in development + return jsonify({"version": "development", "error": str(e)}) + + app.add_url_rule( "/api/init", view_func=InitSessionView.as_view("init", tenant_session), diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..5ca589b --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,73 @@ +# Tenant First Aid Frontend + +React frontend application for the Tenant First Aid chatbot. + +## Development Setup + +See the main [README.md](../README.md) for full setup instructions. + +## Version Display + +The application version is automatically fetched from the backend API and displayed in the navigation sidebar. The version corresponds to the backend version derived from Git tags. + +### Version Hook + +The frontend includes a custom `useVersion` hook that fetches version information from `/api/version`: + +```typescript +import { useVersion } from "./hooks/useVersion"; + +function MyComponent() { + const { version, loading, error } = useVersion(); + + return ( +
+ {loading ? "Loading..." : error ? "Version unavailable" : `v${version}`} +
+ ); +} +``` + +## Development Commands + +From the `frontend/` directory: + +```bash +# Install dependencies +npm install + +# Start development server +npm run dev + +# Build for production +npm run build + +# Lint code +npm run lint + +# Format code +npm run format + +# Preview production build +npm run preview +``` + +## Creating a New Release + +The frontend version automatically reflects the backend version. To create a new release: + +1. Ensure all changes are committed and pushed to the main branch +2. Create and push a new Git tag: + ```bash + git tag v0.3.0 # Follow semantic versioning + git push origin v0.3.0 + ``` +3. The version will automatically be updated and displayed in the UI + +## Architecture + +- **React 19** with TypeScript +- **Vite** for build tooling and development server +- **Tailwind CSS** for styling +- **React Router** for navigation +- **TanStack Query** for API state management \ No newline at end of file diff --git a/frontend/src/hooks/useVersion.tsx b/frontend/src/hooks/useVersion.tsx new file mode 100644 index 0000000..1ea83ce --- /dev/null +++ b/frontend/src/hooks/useVersion.tsx @@ -0,0 +1,35 @@ +import { useState, useEffect } from "react"; + +interface VersionResponse { + version: string; + error?: string; +} + +export function useVersion() { + const [version, setVersion] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchVersion = async () => { + try { + const response = await fetch("/api/version"); + const data: VersionResponse = await response.json(); + + if (response.ok) { + setVersion(data.version); + } else { + setError("Failed to fetch version"); + } + } catch (err) { + setError("Failed to fetch version"); + } finally { + setLoading(false); + } + }; + + fetchVersion(); + }, []); + + return { version, loading, error }; +} \ No newline at end of file diff --git a/frontend/src/pages/Chat/components/Navbar.tsx b/frontend/src/pages/Chat/components/Navbar.tsx index fc5712b..c963597 100644 --- a/frontend/src/pages/Chat/components/Navbar.tsx +++ b/frontend/src/pages/Chat/components/Navbar.tsx @@ -1,9 +1,11 @@ import { useState } from "react"; import { Link } from "react-router-dom"; import TenantFirstAidLogo from "../../../shared/components/TenatFirstAidLogo"; +import { useVersion } from "../../../hooks/useVersion"; export default function Navbar() { const [sidebarOpen, setSidebarOpen] = useState(false); + const { version, loading, error } = useVersion(); return (