Skip to content

Commit baef0d0

Browse files
claude[bot]yangm2
andcommitted
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 <[email protected]>
1 parent 367110c commit baef0d0

File tree

7 files changed

+242
-3
lines changed

7 files changed

+242
-3
lines changed

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,40 @@ Live at https://tenantfirstaid.com/
8989
```
9090
`--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.
9191

92+
## Versioning and Releases
93+
94+
This project uses [semantic versioning](https://semver.org/) with automated version management via `setuptools-scm`. The version is automatically derived from Git tags.
95+
96+
### Creating a Release
97+
98+
1. **Determine the version bump type:**
99+
- **Patch release** (0.2.0 → 0.2.1): Bug fixes, minor improvements
100+
- **Minor release** (0.2.0 → 0.3.0): New features, backward-compatible changes
101+
- **Major release** (0.2.0 → 1.0.0): Breaking changes
102+
103+
2. **Create and push a tag:**
104+
```bash
105+
# For a patch release (default for regular PRs)
106+
git tag v0.2.1
107+
git push origin v0.2.1
108+
109+
# For a minor release (feature additions)
110+
git tag v0.3.0
111+
git push origin v0.3.0
112+
113+
# For a major release (breaking changes)
114+
git tag v1.0.0
115+
git push origin v1.0.0
116+
```
117+
118+
3. **The version will automatically be updated** in the backend API and displayed in the frontend UI.
119+
120+
### Checking the Current Version
121+
122+
- **Backend API**: Visit `/api/version` endpoint
123+
- **Frontend UI**: Check the sidebar navigation (bottom)
124+
- **Command line**: Run `uv run python -c "from importlib.metadata import version; print(version('tenant-first-aid'))"`
125+
92126
## Contributing
93127

94128
We currently have regular project meetups: https://www.meetup.com/codepdx/ . Also check out https://www.codepdx.org/ to find our Discord server.

backend/README.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Tenant First Aid Backend
2+
3+
Flask backend API for the Tenant First Aid chatbot.
4+
5+
## Development Setup
6+
7+
See the main [README.md](../README.md) for full setup instructions.
8+
9+
## API Endpoints
10+
11+
- `/api/init` - Initialize chat session
12+
- `/api/query` - Send chat message
13+
- `/api/history` - Get chat history
14+
- `/api/clear-session` - Clear current session
15+
- `/api/citation` - Get citation information
16+
- `/api/version` - Get application version
17+
18+
## Version Management
19+
20+
This backend uses `setuptools-scm` for automatic version management based on Git tags. The version is dynamically generated from the repository's tag history.
21+
22+
### Checking Version
23+
24+
```bash
25+
# In the backend directory
26+
uv run python -c "from importlib.metadata import version; print(version('tenant-first-aid'))"
27+
28+
# Or via the API
29+
curl http://localhost:5001/api/version
30+
```
31+
32+
### Creating a New Release
33+
34+
1. Ensure all changes are committed and pushed
35+
2. Create a new tag following semantic versioning:
36+
```bash
37+
git tag v0.3.0 # or appropriate version
38+
git push origin v0.3.0
39+
```
40+
3. The version will automatically be updated in the application
41+
42+
## Development Commands
43+
44+
From the `backend/` directory:
45+
46+
```bash
47+
# Install dependencies
48+
uv sync
49+
50+
# Run the server
51+
uv run python -m tenantfirstaid.app
52+
53+
# Run tests
54+
uv run pytest
55+
56+
# Format code
57+
uv run ruff format
58+
59+
# Lint code
60+
uv run ruff check
61+
62+
# Type check
63+
uv run ty check
64+
```

backend/pyproject.toml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "tenant-first-aid"
3-
version = "0.2.0"
3+
dynamic = ["version"]
44
requires-python = ">=3.12"
55
dependencies = [
66
"flask>=3.1.1",
@@ -15,16 +15,21 @@ dependencies = [
1515
"python-dotenv",
1616
"pandas>=2.3.0",
1717
"vertexai>=1.43.0",
18+
"setuptools-scm>=8",
1819
]
1920

2021
[tool.setuptools.packages.find]
2122
where = ["."]
2223
exclude = ["data*"]
2324

2425
[build-system]
25-
requires = ["setuptools>=61"]
26+
requires = ["setuptools>=61", "setuptools-scm>=8"]
2627
build-backend = "setuptools.build_meta"
2728

29+
[tool.setuptools_scm]
30+
version_scheme = "guess-next-dev"
31+
local_scheme = "no-local-version"
32+
2833
[dependency-groups]
2934
dev = [
3035
"ipdb>=0.13.13",

backend/tenantfirstaid/app.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
import os
44
import secrets
55

6+
try:
7+
from importlib.metadata import version
8+
except ImportError:
9+
from importlib_metadata import version
10+
611

712
if Path(".env").exists():
813
from dotenv import load_dotenv
@@ -38,6 +43,16 @@ def clear_session():
3843
return jsonify({"success": True})
3944

4045

46+
@app.get("/api/version")
47+
def get_version():
48+
try:
49+
app_version = version("tenant-first-aid")
50+
return jsonify({"version": app_version})
51+
except Exception as e:
52+
# Fallback if setuptools-scm isn't working or in development
53+
return jsonify({"version": "development", "error": str(e)})
54+
55+
4156
app.add_url_rule(
4257
"/api/init",
4358
view_func=InitSessionView.as_view("init", tenant_session),

frontend/README.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Tenant First Aid Frontend
2+
3+
React frontend application for the Tenant First Aid chatbot.
4+
5+
## Development Setup
6+
7+
See the main [README.md](../README.md) for full setup instructions.
8+
9+
## Version Display
10+
11+
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.
12+
13+
### Version Hook
14+
15+
The frontend includes a custom `useVersion` hook that fetches version information from `/api/version`:
16+
17+
```typescript
18+
import { useVersion } from "./hooks/useVersion";
19+
20+
function MyComponent() {
21+
const { version, loading, error } = useVersion();
22+
23+
return (
24+
<div>
25+
{loading ? "Loading..." : error ? "Version unavailable" : `v${version}`}
26+
</div>
27+
);
28+
}
29+
```
30+
31+
## Development Commands
32+
33+
From the `frontend/` directory:
34+
35+
```bash
36+
# Install dependencies
37+
npm install
38+
39+
# Start development server
40+
npm run dev
41+
42+
# Build for production
43+
npm run build
44+
45+
# Lint code
46+
npm run lint
47+
48+
# Format code
49+
npm run format
50+
51+
# Preview production build
52+
npm run preview
53+
```
54+
55+
## Creating a New Release
56+
57+
The frontend version automatically reflects the backend version. To create a new release:
58+
59+
1. Ensure all changes are committed and pushed to the main branch
60+
2. Create and push a new Git tag:
61+
```bash
62+
git tag v0.3.0 # Follow semantic versioning
63+
git push origin v0.3.0
64+
```
65+
3. The version will automatically be updated and displayed in the UI
66+
67+
## Architecture
68+
69+
- **React 19** with TypeScript
70+
- **Vite** for build tooling and development server
71+
- **Tailwind CSS** for styling
72+
- **React Router** for navigation
73+
- **TanStack Query** for API state management

frontend/src/hooks/useVersion.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { useState, useEffect } from "react";
2+
3+
interface VersionResponse {
4+
version: string;
5+
error?: string;
6+
}
7+
8+
export function useVersion() {
9+
const [version, setVersion] = useState<string | null>(null);
10+
const [loading, setLoading] = useState(true);
11+
const [error, setError] = useState<string | null>(null);
12+
13+
useEffect(() => {
14+
const fetchVersion = async () => {
15+
try {
16+
const response = await fetch("/api/version");
17+
const data: VersionResponse = await response.json();
18+
19+
if (response.ok) {
20+
setVersion(data.version);
21+
} else {
22+
setError("Failed to fetch version");
23+
}
24+
} catch (err) {
25+
setError("Failed to fetch version");
26+
} finally {
27+
setLoading(false);
28+
}
29+
};
30+
31+
fetchVersion();
32+
}, []);
33+
34+
return { version, loading, error };
35+
}

frontend/src/pages/Chat/components/Navbar.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { useState } from "react";
22
import { Link } from "react-router-dom";
33
import TenantFirstAidLogo from "../../../shared/components/TenatFirstAidLogo";
4+
import { useVersion } from "../../../hooks/useVersion";
45

56
export default function Navbar() {
67
const [sidebarOpen, setSidebarOpen] = useState(false);
8+
const { version, loading, error } = useVersion();
79

810
return (
911
<nav className="fixed top-0 left-0 w-full bg-[#1F584F] shadow-md py-3 px-6 z-50">
@@ -44,7 +46,7 @@ export default function Navbar() {
4446
sidebarOpen ? "translate-x-0" : "translate-x-full"
4547
}`}
4648
>
47-
<div className="flex flex-col p-8 gap-6 mt-10">
49+
<div className="flex flex-col h-full p-8 gap-6 mt-10">
4850
<Link
4951
to="/"
5052
className="block px-3 py-2 rounded text-gray-700 font-medium transition-colors hover:bg-[#4F8B82] hover:text-[#F4F4F2]"
@@ -74,6 +76,17 @@ export default function Navbar() {
7476
Privacy Policy
7577
</Link>
7678
<hr className="my-2 border-t border-gray-300" />
79+
<div className="mt-auto pb-4">
80+
<div className="text-xs text-gray-500 text-center">
81+
{loading ? (
82+
"Loading version..."
83+
) : error ? (
84+
"Version unavailable"
85+
) : (
86+
`v${version}`
87+
)}
88+
</div>
89+
</div>
7790
</div>
7891
</div>
7992

0 commit comments

Comments
 (0)