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..e73ac04 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,8 @@ dependencies = [ "python-dotenv", "pandas>=2.3.0", "vertexai>=1.43.0", + "setuptools-scm>=8", + "importlib-metadata>=8.7.0", ] [tool.setuptools.packages.find] @@ -28,7 +30,7 @@ where = ["."] exclude = ["data*"] [build-system] -requires = ["setuptools>=61"] +requires = ["setuptools>=80", "setuptools-scm>=8"] build-backend = "setuptools.build_meta" [dependency-groups] diff --git a/backend/tenantfirstaid/app.py b/backend/tenantfirstaid/app.py index b9cf5e7..b2d1853 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,23 @@ def clear_session(): return jsonify({"success": True}) +@app.get("/api/version") +def get_version(): + try: + # Try the package name as defined in pyproject.toml + app_version = version("tenant-first-aid") + return jsonify({"version": app_version}) + except Exception: + try: + # Try alternative package name format + app_version = version("tenant_first_aid") + return jsonify({"version": app_version}) + except Exception: + # Fallback for development or when setuptools-scm can't determine version + # This happens when there are no git tags yet + return jsonify({"version": "0.1.0-dev"}) + + app.add_url_rule( "/api/init", view_func=InitSessionView.as_view("init", tenant_session), diff --git a/backend/uv.lock b/backend/uv.lock index e92d562..0d938a7 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -724,6 +724,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, ] +[[package]] +name = "importlib-metadata" +version = "8.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" }, +] + [[package]] name = "iniconfig" version = "2.1.0" @@ -1735,6 +1747,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ac/fd/669816bc6b5b93b9586f3c1d87cd6bc05028470b3ecfebb5938252c47a35/ruff-0.12.9-py3-none-win_arm64.whl", hash = "sha256:63c8c819739d86b96d500cce885956a1a48ab056bbcbc61b747ad494b2485089", size = 11949623, upload-time = "2025-08-14T16:08:52.233Z" }, ] +[[package]] +name = "setuptools" +version = "80.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, +] + +[[package]] +name = "setuptools-scm" +version = "8.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "setuptools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/19/7ae64b70b2429c48c3a7a4ed36f50f94687d3bfcd0ae2f152367b6410dff/setuptools_scm-8.3.1.tar.gz", hash = "sha256:3d555e92b75dacd037d32bafdf94f97af51ea29ae8c7b234cf94b7a5bd242a63", size = 78088, upload-time = "2025-04-23T11:53:19.739Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/ac/8f96ba9b4cfe3e4ea201f23f4f97165862395e9331a424ed325ae37024a8/setuptools_scm-8.3.1-py3-none-any.whl", hash = "sha256:332ca0d43791b818b841213e76b1971b7711a960761c5bea5fc5cdb5196fbce3", size = 43935, upload-time = "2025-04-23T11:53:17.922Z" }, +] + [[package]] name = "shapely" version = "2.1.1" @@ -1860,7 +1894,6 @@ wheels = [ [[package]] name = "tenant-first-aid" -version = "0.2.0" source = { editable = "." } dependencies = [ { name = "flask" }, @@ -1871,11 +1904,13 @@ dependencies = [ { name = "google-cloud-aiplatform" }, { name = "google-genai" }, { name = "gunicorn" }, + { name = "importlib-metadata" }, { name = "jsonlines" }, { name = "openai" }, { name = "pandas" }, { name = "python-dotenv" }, { name = "redis" }, + { name = "setuptools-scm" }, { name = "simplejson" }, { name = "valkey" }, { name = "vertexai" }, @@ -1911,11 +1946,13 @@ requires-dist = [ { name = "google-cloud-aiplatform", specifier = ">=1.106.0" }, { name = "google-genai", specifier = ">=1.28.0" }, { name = "gunicorn", specifier = ">=23.0.0" }, + { name = "importlib-metadata", specifier = ">=8.7.0" }, { name = "jsonlines" }, { name = "openai", specifier = "==1.89" }, { name = "pandas", specifier = ">=2.3.0" }, { name = "python-dotenv" }, { name = "redis", specifier = ">=6.4.0" }, + { name = "setuptools-scm", specifier = ">=8" }, { name = "simplejson" }, { name = "valkey", specifier = ">=6.1.0" }, { name = "vertexai", specifier = ">=1.43.0" }, @@ -2280,3 +2317,12 @@ sdist = { url = "https://files.pythonhosted.org/packages/da/9a/3b29831d8617ecbcf wheels = [ { url = "https://files.pythonhosted.org/packages/93/ca/d53764f0534ff857239595f090f4cb83b599d226cc326c7de5eb3d802715/xhtml2pdf-0.2.17-py3-none-any.whl", hash = "sha256:61a7ecac829fed518f7dbcb916e9d56bea6e521e02e54644b3d0ca33f0658315", size = 125349, upload-time = "2025-02-24T20:44:42.604Z" }, ] + +[[package]] +name = "zipp" +version = "3.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, +] diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..662b99a --- /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 ( +